fotoxx-15.11.1/0000775000175000017500000000000012616075370011660 5ustar micomicofotoxx-15.11.1/doc/0000755000175000017500000000000012616075370012423 5ustar micomicofotoxx-15.11.1/doc/fotoxx.man0000644000175000017500000000702612616075370014454 0ustar micomico.TH FOTOXX 1 2015-11-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 Fotoxx is a GUI program which operates in its own window. Organize and administer a collection of images, edit images, search images, and perform utility functions. .SH OVERVIEW Image edit functions include: - View and edit most image and RAW file types - Adjust brightness/color/contrast using movable curves - Expand and/or flatten the brightness distribution - Trim (crop), Resize, Flip, Rotate (any angle) - Sharpen, Blur, Reduce noise, Remove red-eyes - Tone Mapping (enhance local contrast and faint details) - Auto adjust white balance, Match colors to a standard - 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 - Compose edit scripts and use as batch edit function - Edit selected objects or areas separately within an image - Copy and paste selected objects or areas across images - Art effects (drawing, painting, cartoon, 3D relief, others) - Edit and apply 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) - Montage: Arrange images and formatted text in a layout - Panorama, HDR and Stack composites (handheld camera OK) - Combine multiple photos with varying focus depths (HDF) - 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 - Custom graphic menu with user-selected 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 (thumbnails with text) - Use geotags from camera GPS, add/edit geotags manually - Search images by location, show in thumbnail gallery - Click on a map to see all images from or near that location - Batch convert format, resize, move, export, burn DVD/BlueRay - Batch rename images using a base name and sequence number - Batch convert RAW files to tiff-8/16, png-8/16 or jpeg - Print image at any size for any paper format - Calibrate printer for more accurate printed colors - Create albums (views), select and arrange images - Slide-show with animated transitions and image zoom-in - 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 online user manual is available using the Help menu. This manual explains Fotoxx operation in great detail. The home page for Fotoxx is http://kornelix.com/fotoxx. .SH AUTHORS Written by Mike Cornelison http://kornelix.com fotoxx-15.11.1/doc/changelog0000644000175000017500000004466412616075370014313 0ustar micomicoFotoxx Change Log http://www.kornelix.com/fotoxx ================= 2015 Nov 03 v.15.11.1 + Bugfix: Rename failed to automatically open the next following file. + Translation updates: Spanish, Catalan, Portuguese. 2015 Nov 01 v.15.11 + Folders in gallery pages show contained sub-folder and image file counts. + Pattern: pattern can imprint target image without changing image colors. + Adjust HSL: the amount of color change is proportional to the match with a selected target color, and this match range is adjustable. + Batch Convert: more flexibility in the formatting of output file names. + New: Spherical image projection with variable radius of curvature. + Trim/Rotate: if an image is leveled using mouse drag, the trim rectangle is automatically maximized, removing the need to do this manually. + If an edit dialog is started and left/right image margins are present, the image is pushed to the right side so the dialog can be positioned over the expanded left margin and menu panel, to cover less image area. This position is saved and restored the next time the dialog is used. + Saving to file type .bmp was removed, since all metadata is lost. + A new transition type was added to Slide Show: collapsing sphere. + Six other minor improvements and bug fixes. 2015 Oct 09 v.15.10 + New: Batch Scripts: Perform a series of image edits while recording all menu and dialog inputs into a script file. Execute the script file to apply the same edits to multiple images selected from gallery pages. + Calibrate Printer: improved for slightly more accurate colors. + Edit curves: The capture distance for mouse selection of nodes to drag is adjustable (increase default value to make touch pads easier to use). + Batch Add/Change Metadata: a pick-list for commonly used tags was added. + Geographic maps: mouse distance to trigger a popup location name was made independent of map scale (10 pixels instead of 10 km). + Bugfix: gallery file selection: clicking on a file name in the list of selected files was sometimes inaccurate and selected the wrong file. + Bugfix: setting geotags by clicking on map locations did not allow an initial location to be revised by clicking somewhere else. 2015 Sep 03 v.15.09.2 + Bugfix: infinite loop when saving RAW file to JPG/TIF/PNG. + Bugfix: Open of TIF file with 8 bits/color showed 0 or 16 in top panel. 2015 Sep 01 v.15.09 + Program code was revised for latest GTK API breakage (Ubuntu 15.10). + The font size used in menus and dialogs can now be set by the user. + Panorama: about 20% faster (3 x 7 megapixels, 2 GHz CPU --> 18 secs). + Paint Transparency: response lag with large images was eliminated. + Area Copy and Paste was reinstated (easier than file save and open). + Use of alpha channel for vacated margins extended to all bend functions. + New: Calibrate Printer: tool to improve image print color accuracy. + Bugfix: crash in Flatten Book Page while marking page edges. + Bugfix: Slide Show fails if translated transition names contain blanks. + Bugfux: Leverage Edit: use of contrast as edit lever was broken. + Seven other minor improvements and fixes. 2015 Aug 01 v.15.08 + The brightness distribution for an image can be reshaped by using slider controls and watching the image and/or graph. The distribution can be clipped at either end, or stretched or flattened in different zones. + Editing and searching image tags have an additional tool to deal with a huge list of defined tags. The tag pick-list can be all tags, or only those for a category chosen from a category pick-list. + Select Area: open/save area cutout as PNG file with transparency data. + Image edits: image transparency data is now utilized and retained. + Paint Transparency: new: paint [semi-] transparent areas on an image. + Mashup: transparency data in overlay images is utilized and editable. + New batch tool: add/change/delete any metadata for selected image files. + Add Text: quickly add same text at same position to multiple image files. + Gallery thumbnail memory cache is self-limiting to 1 GB main memory. + Several small improvements in the user interface and error messages. + Bugfix: gallery sort by file modification date used incorrect date/time. + Bugfix: possible crash in leverage edits if edit function cancelled. + Bugfix: Select Area: draw/replace area edge did not work properly for areas selected by mouse painting or areas previously finished. 2015 Jul 01 v.15.07 + Menus were reorganized. User Guide was revised to match. + User Guide was audited and many minor errors were corrected. + Index Image Files: user interface and messages were rationalized. + Undo/Redo button: middle mouse button produces a popup list of edits done to the current image. Go back to any done or undone edit step. + Edit Metadata and Search Images: working with thousands of unorganized image tags was made much more practical: type-in tags or use pick-list. + New batch tool: convert tags for all image files using a from-to list. + New batch tool: select images (click thumbnails) to trash or delete. + New batch tool: find and upright all image files rotated 90 degrees. + Gallery navigation panel: parent directories are buttons instead of text. + Effects > Pencil Sketch: set any desired foreground or background color. + RAW image noise measure was improved (gives slightly lower values). + Select Area: new buttons to quickly change line color while drawing. + Slide Show: zoom-out from a chosen image spot (compliment of zoom-in). + New user option: start session with last gallery now works for albums. + 4 minor bug fixes and 10 user interface improvements. 2015 Jun 01 v.15.06 + New: HSL Color: change the color of selected image areas using an HSL color chooser. Blend with original image to preserve existing variation. + Threshold Denoise algorithm was improved. + Cycle Desktop (background image): Runs in the background. Add to the startup list to get a new desktop image each logon or time period. + New: Set Desktop Image: Set the desktop background image from Fotoxx. + New slide show transition: Image falls over to reveal the next image. + Mashup: UI improved for adding text and lines/arrows to a layout. + Trim/Rotate now includes an [auto] button to remove the black image margins left by composite or warp functions. Auto-Trim menu is gone. + KB shortcuts includes a button to report all the standard shortcuts. + Multiple UI and help text improvements. + Three minor bug fixes. 2015 May 01 v.15.05 + New: Smart Denoise: measure noise and use the result to reduce noise with minimal loss of subtle detail. Also measure camera sensor noise. + The function Newest Images was revised to show a gallery of the newest 1000 files, based on either EXIF photo date or file modification date. + Zonal Flatten: algorithm improvement and stronger deband controls. + New: Cycle Background: cycle desktop background image via Fotoxx album. + Bugfix: possible crash in some edit functions if [done] button pressed very quickly after adjusting a dialog control or edit curve. + Bugfix: Edit Metadata: tags with special characters were sometimes getting split into two tags. + Bugfix: Color Mode: color negative: it was sometimes possible to create invalid RGB colors which affected later color functions. 2015 Apr 08 v.15.04.1 Bugfix: Plugin function crash if used for the first edit of an image file. 2015 Apr 01 v.15.04 + New: Zonal Flatten: enhance contrast in dull areas even when overall brightness and contrast is well optimized (powerful, try it). + New: Directed Blur: 1-dimensional image blur in direction of mouse drag. + Properly extract thumbnails from RAW files enclosing .ppm thumbnails. + Album edit: View any image full size while adding and arranging images. + Slide Show: Support for stop show, jump to a new position, resume show. + Slide Show: New image transitions: implode, explode, Japanese fan. + Print dialog accepts margin inputs in either inches or centimeters. + New user setting: show or hide hidden directories in gallery view. + Bugfix: print dialog: displayed print size was not always correct. 2015 Mar 02 v.15.03.1 + Portuguese, Spanish, Catalan, German translations updated. + Bugfix: Mashup: recover lost fine-positioning function via KB arrow keys. + Bugfix: Mashup: lock menus to avoid crash from unconventional user action. + Bugfix: Upright: properly upright image in window, keep dialog active. 2015 Mar 01 v.15.03 + "Rotate 90" was renamed "Upright". A button was added that detects and correct 90/180° rotations without guidance (based on EXIF data). + Batch Convert and Batch Upright detect and correct 90/180° rotations. + The term "Named Collection" was replaced with "Album", which is the term most commonly used for this type of functionality. + Smart Erase got an IQ upgrade to better deal with curvy objects. + Mashup: Black margins in overlay images (from prior warping edits or panorama composite) can be automatically removed (made transparent). + Mashup: Editing very large layouts was made more responsive. + Mashup: Overlay images can be warped by dragging the mouse. This can also be used to fix minor alignment offsets from Panorama Tools. + Mashup: Overlay image margins can be adjusted two ways: hard edge trim and/or a gradual edge blend with background or other overlapped images. + The window title bar and gallery pages show photo time as well as date. + New slide show transition: image shrinks away to expose the next image. + Three minor bug fixes and two minor usability improvements. 2015 Feb 24 v.15.02.1 Bugfix: Fotoxx may fail to start after a new install if there are less than 200 image files found or if file "newest_files" has been deleted. 2015 Feb 01 v.15.02 + The indexing process for newly added image files was made faster: speedup is about 1.2x for a 7200 rpm disk and 1.9x for an SSD. + Panorama: a multi-row panorama capability was added by integrating Panorama Tools utilities into the Fotoxx menus. Very easy to use. + Slide Show: the maximum zoom-in speed was increased by 2x. This is significant for a large or high DPI monitor or a slow computer. + Slide Show: the option to show captions/comments can be configured per image, and they can be made to vanish after a specified interval. + User Settings: Thumbnail size is adjustable over 128/256/512 pixels (default 256). 512 is a bit slower but better for a large or high DPI monitor, or when viewing gallery pages with thumbnail size at maximum. + Mashup: mouse-painted image transparencies are easier to control. + Mashup: transparency blends can be set separately for all 4 image edges. + Cosmetic improvements in gallery view and metadata short report. + Startup parameter -nosync was replaced with the more correct -noindex. + Bugfix: quick rotate 90 degrees: metadata was not being copied. + Bugfix: crash if switch to gallery view during slide show transition. + Bugfix: HDR/HDF/Stack: crash if [cancel] pressed twice rapidly. 2015 Jan 17 v.15.01.1 + French translation updated. + Two minor bug fixes. 2015 Jan 01 v.15.01 + Panorama: better image projection math for more precise stitching. + Several slider controls were given a log scale for easier and more precise control of small changes near the neutral value. + Reduce Noise: new option: selectively apply to darker image areas. + Adding and editing plugins was made easier. + Plugins menu was moved from the window left panel into the Edit menu. + Gallery view has a small layout change for improved appearance. + Gallery popup windows: F11 toggles full screen view, Escape terminates. + Magnify function: user interface was made more flexible. + Slide Show: zoom-in limit raised to 3x, image retains full sharpness. + Map View: red dots are sized for good visibility at any map scale. + Bugfix: Printing an unsaved edited image file printed the original file. 2014 Dec 01 v.14.12 + Tools > Magnify Image: New function - analogous to inspecting a printed image with a magnifying glass that can be moved freely over the image. + Tools > User Settings: When stepping through image files with the prev/next button or arrow keys, there is a new option to show only the final edited version (or the original file if never edited). + Retouch Combo: The slider to emphasize darker...brighter pixels for color adjustments was made more effective. + New command parameter -nosync: Allows a faster startup, but search and map functions are disabled because of the risk of incorrect results. More information is in the Technical Notes section of the User Guide. + Select Area: The Finish dialog has a new option to extend the selection automatically to designated image corners - a time saver. + Slide Show: When paused, the Keyboard M-key starts the magnify function. + Bugfix: Edit Metadata was not being cancelled when a conflicting function was started. + Bugfix: Adding geotags by clicking on a map sometimes failed. + Bugfix: Copy text from a dialog widget with Ctrl+C was not working. 2014 Nov 01 v.14.11 + A world map and several larger-scale continental maps are provided in a separate package: fotoxx-maps. These maps show locations of photos having geotags. Clicking a map location shows a gallery of thumbnail images that can be selected in turn for viewing full size or editing. A user may add more maps, and these will work the same way. + Gallery thumbnail popup: expanded speed and functionality for zooming thumbnails and comparing images side by side. + Gallery scrolling is smoother and easier to control with the mouse: scrolls faster as mouse is dragged closer to top or bottom of window. + Sync Gallery can now have a KB shortcut (default 'S' for new installs). + Bugfix: Edit Geotags: If the dialog inputs were cleared to blanks, the image geotag data was not being erased. 2014 Oct 09 v.14.10.2 + Bugfix: If an unmodified image was saved with a different file type, the wrong extension was used, e.g. jpeg file saved as filename.png. 2014 Oct 02 v.14.10.1 + Bugfix: Upgrade from an older version of Fotoxx may make the window menus invisible. Workaround: delete the line "menu style" in the file /home//.fotoxx/parameters. + Translation updates for Spanish, Catalan, Italian, Portuguese, Russian, French. 2014 Oct 01 v.14.10 + Make Waves effect: Horizontal and vertical wavelength, magnitude and variance can be set independently. More realistic appearance. + Drawing lines and arrows on an image or mashup was made easier: create a line or arrow and then drag the end-points into final position. + Gallery popup image improvements: window fits image without margins, window and image can be scaled up or down with the mouse wheel. + New setting: Monitor scale: adjust panel scale for high DPI monitor. + Bugfix: Image date and size in the search index were not always updated when changed (this error fixed itself at the next Fotoxx startup). + Bugfix: The Print Image function with grid lines enabled did not print the horizontal grid lines clearly (adjacent black and white lines). + Bugfix: The Shift Colors function [reset] button reset the dialog controls but failed to reset the image edits. + Bugfix: Mashup crash when deleting and adding text in a mixed sequence. 2014 Sep 01 v.14.09 This release makes improvements and fixes annoyances in the user interface. + Dialogs set focus on the main window after use of the [apply] or [next] button, so subsequent KB shortcuts will work without extra mouse action. + File Save: both unsaved metadata edits and image edits are now saved. (using [apply] in a metadata dialog saves only the metadata changes). + Display "no more images" after deleting the last image in the current gallery (otherwise the window goes inexplicably blank). + Additional dialogs now restore previous inputs across sessions. + Search Images: if time is omitted in a date/time search range, then 00:00 or 23:59 is defaulted for the start or end time respectively. + Search Images: new option: find only the latest version of each image. + Batch Convert: If image files are renamed or moved with deletion of the originals, collections containing the images are updated automatically. (The problematic Move Collections function was removed.) + Gallery popup images: use the same window or open a new window for each thumbnail clicked. The one window option is effectively a combined image and gallery view, with adjustable allocation of screen space between the two views, and one-click selection of the next image to view. + Area Select: select by matching colors was made a little smarter. + Gallery Selection: scroll selection list to keep new insertions in view. + New utility function to find and display all duplicate images, fast. + Slide Show: option to auto-replay after reaching the end. + Bugfix: Manage Collections: dragging a thumbnail to the bottom of the gallery window would not scroll up if the window was maximized. 2014 Aug 01 v.14.08 + The user interface for creating or replacing a collection was improved. + Images within a collection can now be rearranged via mouse drag and drop. + New effect: Make Waves: add random waviness to an image or selected area. + The Undo/Redo button combined with the 'A' key is Undo/Redo all edits. + Editing metadata in RAW images is now allowed (restriction was removed). + A few non-obvious dialog controls now have popup tips when moused-over. + Open Previous File retains zoom size/position to facilitate comparisons. + The menu icon size is now adjustable in Tools > User Settings. 2014 Jul 02 v.14.07.1 + Spanish and Catalan translations were updated. + Bugfix: stop spurious extra menu entry in non-English locales. 2014 Jul 01 v.14.07 + An image with selected areas can be saved to a PNG file with unselected parts transparent. This can be useful in other image editors like Gimp. + The main menu was made smaller to save space. Some menu-icons now use left/right mouse clicks for two functions, e.g. edit undo/redo. + Mashup: draw lines and arrows on an image, similarly to writing text. + Mashup: control of overlapping and transparent images was made easier. + A batch upright tool was added - find and upright images turned 90°. + Panorama: image matching for exposure/color differences was improved. + The current image on the gallery page is more strongly highlighted. + Raw Therapee was added as a RAW image file processing option. + Metadata and geotag edits were added to the right-click popup menus. + The world map was made 2x larger in area (a new download is required). + Bugfix: file rename could sometimes leave an incorrect thumbnail. + Bugfix: Batch RAW did not set the sRGB color space as default. + Bugfix: memory leak in Mashup function. fotoxx-15.11.1/doc/README-es0000644000175000017500000000434112616075370013712 0ustar micomicoInstalación de Fotoxx desde el tarball fuente   Compilar Fotoxx requiere los siguientes paquetes:      g++ el compilador y enlazador C++ de GNU      libgtk3.0-dev librería gráfica GTK3 (GUI base)      libtiff4-dev lectura/escritura de archivos de imagen tiff-8/16      libpng12-dev lectura/escritura de archivos de imagen png-8/16   Para la ejecución los siguientes paquetes también son necesarios:      exiftool lectura/escritura de metadatos (EXIF, etc)      dcraw conversión de archivos de cámara RAW a TIFF      ufraw interfaz gráfica de usuario para dcraw      xdg-utils utilidades Linux estándar LSB      brasero necesario para grabar imágenes en un CD / DVD   Los anteriores son los últimos nombres para Ubuntu. El nombrado de paquetes es caótico  así que puede que tenga que dar caza a los más adecuados para su distribución.   Compilar e instalar Fotoxx de la siguiente manera:.      1. Descargue el archivo tar (Fotoxx-N.N.tar.gz) en el Escritorio      2. Abra una ventana de terminal      3. $ Cd Escritorio # vaya al escritorio      4. $ Tar-xzf Fotoxx-N.N.tar.gz # descomprimir a ./fotoxx      5. $ Cd fotoxx # ir a directorio fotoxx      6. $ Make # compilar programa      7. $ Sudo make install # instalar el programa   Las dependencias no satisfechas causarán un mensaje de error en el paso 6. Instale éstas desde su repositorio y repita el paso 6.   El paso 7 mueve todos los archivos a las ubicaciones siguientes:      /usr/bin/fotoxx binario ejecutable      /usr/share/fotoxx/ iconos, traducciones ...      /usr/share/doc/fotoxx/ guía de usuario, README ... Para el paso 7, utilice "sudo" o "su-c" para obtener privilegios de root.   Por favor revise la guía del usuario (menú Ayuda) antes de probar Fotoxx.   NOTAS PARA CONSTRUCTORES DE PAQUETE (PACKAGE):   Si $PREFIX está definido, los archivos irán allí en vez de a /usr.   Si $DESTDIR también está definido, los archivos irán a $DESTDIR$PREFIX.   exiftool (el nombre del paquete varía) debe ser la versión 8.60 o posterior fotoxx-15.11.1/doc/README-ca0000644000175000017500000000361412616075370013670 0ustar micomico Instal·lació de Fotoxx des del tarball font Compilar Fotoxx requereix els següents paquets: g++ el compilador i enllaçador C ++ de GNU libgtk3.0-dev llibreria gràfica GTK3 (GUI base) libtiff4-dev lectura/escriptura d'arxius d'imatge tiff-8/16 libpng12-dev lectura/escriptura d'arxius d'imatge png-8/16 Per l'execució dels següents paquets també són necessaris: exiftool lectura/escriptura de metadades (EXIF, etc) dcraw conversió d'arxius de càmera RAW a TIFF UfRAW interfície gràfica d'usuari per dcraw xdg-utils utilitats Linux estàndard LSB braserO necessari per gravar imatges en un CD/DVD Els anteriors són els últims noms per Ubuntu. El nomenat de paquets és caòtic així que potser hagi de donar caça als més adequats per a la seva distribució. Compilar i instal·lar Fotoxx de la següent manera: 1. Descarregueu el tarball (Fotoxx-N.N.tar.gz) en Escriptori 2. Obriu una finestra de terminal 3. $ Cd Escriptori # vagi a l'escriptori 4. $ Tar-xzf Fotoxx-N.N.tar.gz # descomprimir a ./ Fotoxx 5. $ Cd fotoxx # anar al directori fotoxx 6. $ Make # compilar programa 7. $ Sudo make install # instal·lar el programa Les dependències no satisfetes causaran un missatge d'error en el pas 6. Instal.leu aquestes des del seu repositori i repetiu el pas 6. El pas 7 mou tots els arxius a les ubicacions següents: /usr/bin/fotoxx binari executable /usr/share/fotoxx/ icones, traduccions ... /usr/share/doc/fotoxx/ guia d'usuari, README ... Per al pas 7, utilitzeu "sudo" o "su-c" per obtenir privilegis de root. Si us plau, lLlegiu la guia d'usuari (menú Ajuda) abans de provar Fotoxx. NOTES PER ALS CONSTRUCTORS DEL PAQUET (PACKAGE): Si $PREFIX està definit, els arxius aniran allà en comptes de a /usr. Si $DESTDIR també està definit, els arxius ani a $DESTDIR$PREFIX. exiftool (el nom del paquet varia) ha de ser la versrió 8.60 o posterior fotoxx-15.11.1/doc/README-en0000644000175000017500000000376012616075370013711 0ustar micomicoInstallation of fotoxx from source tarball Building fotoxx requires the following packages: g++ the Gnu C++ compiler and linker libgtk3.0-dev GTK3 graphics library (GUI base) libtiff*-dev read/write tiff-8/16 image files libpng*-dev read/write png-8/16 image files liblcms*-dev change color space (sRGB/Adobe RGB) At run time the following packages are also needed: exiftool read/write metadata (EXIF etc.) dcraw convert camera RAW files to TIFF xdg-utils LSB standard Linux utilities binutils GNU binary utilities The above are recent Debian names. Package naming is chaotic so you may have to to hunt down the names for your distro. Build and install fotoxx as follows: 1. Download the tar file (fotoxx-N.N.tar.gz) to Desktop 2. Open a terminal window 3. $ cd Desktop # go to Desktop 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 ... For step 7, use "sudo" or "su -c" for root privileges. Please review the user guide (Help menu) before trying fotoxx. NOTE: The optional fotoxx-maps package should be installed only after fotoxx is installed. NOTES FOR PACKAGE BUILDERS: If $PREFIX is defined, files go there instead of /usr. If $DESTDIR is also defined, files go to $DESTDIR$PREFIX. exiftool (package name varies) must be version 8.60 or later. Please do not move any files into an optional "documentation" or "data" package. This would make Fotoxx non-functional. fotoxx-15.11.1/doc/copyright0000644000175000017500000000077612616075370014370 0ustar micomicoAuthor: Michael Cornelison Copyright: Copyright 2007-2015 Michael Cornelison The source program code can be found here: http://kornelix.com/tarballs License: You are free to distribute this software under the terms of the GNU General Public Licensel, either version 3 of the License, or (at your option) any later version. The complete text of the license can be found here: http://www.gnu.org/licenses/gpl-3.0.html See "/usr/share/common-licenses/GPL-3". fotoxx-15.11.1/f.batch.cc0000664000175000017500000030121712616075370013500 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** Fotoxx image edit - Batch 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_dcraw convert RAW files using DCraw m_batch_rawtherapee convert RAW files using Raw Therapee m_scriptfiles script files for batch editing m_burn burn images to DVD/Blue-Ray disc m_duplicates find all duplicated images ***************************************************************************/ #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 { char **filelist; int Fsametype, Fsamesize, maxww, maxhh; int Fdelete, Fcopymeta, Fupright, Fsharpen; char newloc[500], newname[200], newext[8]; int filecount, baseseq, addseq; int amount, thresh; }; 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; char *inloc, *inname, *inext; char *outloc, *outname, *outext; char *tempname; char **oldfiles, **newfiles; char seqnum[20]; char *pp1, *pp2, *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; cchar *exifkey[1] = { exif_orientation_key }; cchar *exifdata[1]; 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 [___] | | | | [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,"entry","newname","hbname",0,"expand|size=30"); 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,"entry","baseseq","hbseq",0,"size=5"); zdialog_add_widget(zd,"label","labadder","hbseq",ZTX("adder"),"space=5"); zdialog_add_widget(zd,"entry","addseq","hbseq",0,"size=3"); zdialog_add_widget(zd,"label","space","hbseq",0,"space=50"); // force big entry widgets smaller 15.11 zdialog_add_widget(zd,"hbox","hbloc","dialog"); zdialog_add_widget(zd,"label","labloc","hbloc",ZTX("New Location"),"space=5"); zdialog_add_widget(zd,"entry","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,"entry","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,"entry","maxhh","hbwh","700","size=5"); zdialog_add_widget(zd,"check","samesize","hbwh",ZTX("no change"),"space=12"); 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=3"); zdialog_add_widget(zd,"check","upright","hbopts",ZTX("Upright"),"space=3"); 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",ZTX("amount"),"space=3"); zdialog_add_widget(zd,"spin","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,"spin","thresh","hbsharp","0|100|1|20"); 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); filelist = 0; filecount = 0; *newloc = 0; oldfiles = newfiles = 0; Noldnew = 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 (! filecount) goto cleanup; free_resources(); // no curr. file 15.03 m_viewmode(0,"F"); gallery_monitor("stop"); // stop tracking gallery updates if (Fdelete) { cc = filecount * 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 } write_popup_text("open","Processing files",500,200,Mwin); // status monitor popup window for (ii = 0; ii < filecount; ii++) // loop selected files { infile = filelist[ii]; // 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 = (char *) zmalloc(8); cc = strlen(outloc) - 1; // remove trailing '/' if (outloc[cc] == '/') outloc[cc] = 0; if (*newname) { zfree(outname); outname = zstrdup(newname); } if (*newname && strstr(newname,"[oldname]")) // 15.11 { zfree(outname); outname = (char *) zmalloc(strlen(newname) + strlen(inname)); // .....[oldname]..... pp1 = strstr(newname,"[oldname]"); // | | pp2 = pp1 + 9; // pp1 pp2 cc = pp1 - newname; strncpy(outname,newname,cc); strcpy(outname+cc,inname); cc += strlen(inname); strcpy(outname+cc,pp2); } if (strchr(outname,'#')) // sequence number needed 15.11 { pp1 = strchr(outname,'#'); // .....#####..... pp2 = pp1 + 1; // | | while (*pp2 == '#') pp2++; // pp1 pp2 cc = pp2 - pp1; jj = baseseq + ii * addseq; // new sequence number nnnnnn snprintf(seqnum,20,"%0*d",cc,jj); cc = strlen(seqnum); tempname = (char *) zmalloc(strlen(outname) + cc + 2); cc = pp1 - outname; strncpy(tempname,outname,cc); strcpy(tempname + cc, seqnum); strcat(tempname + cc, pp2); 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); write_popup_text("write",outfile); // log each output file zmainloop(); if (*newloc) { err = stat(outfile,&statdat); // check if file exists in new location if (! err) { write_popup_text("write",Bfileexists); zfree(outfile); continue; } } pxmin = PXM_load(infile,0); // read input file if (! pxmin) { write_popup_text("write",ZTX("file type not supported")); 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. 15.03 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); tempfile = zstrdup(infile,12); // temp file needed for EXIF/IPTC copy pp1 = strrchr(outfile,'.'); pp2 = strrchr(tempfile,'.'); strcpy(pp2,"-temp"); strcpy(pp2+5,pp1); 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) { write_popup_text("write",ZTX("cannot create new file")); zfree(outfile); zfree(tempfile); 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 = shell_ack("cp -p \"%s\" \"%s\" ",tempfile,outfile); // copy tempfile to output file if (err) write_popup_text("write",wstrerror(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 } 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); zfree(tempfile); PXM_free(pxmout); } if (Noldnew) { // update albums for renamed/moved files write_popup_text("write",ZTX("updating albums ...")); conv_albums(oldfiles,newfiles,Noldnew); } write_popup_text("write","COMPLETED"); if (navi::gallerytype == 1) // update gallery file list 15.03 gallery(0,"init"); gallery_monitor("start"); // resume tracking gallery updates cleanup: if (filecount) { // free memory for (ii = 0; ii < filecount; ii++) zfree(filelist[ii]); zfree(filelist); filelist = 0; filecount = 0; } if (Noldnew) { for (ii = 0; ii < Noldnew; ii++) { zfree(oldfiles[ii]); zfree(newfiles[ii]); } zfree(oldfiles); zfree(newfiles); Noldnew = 0; } Fblock = 0; return; } // dialog event and completion callback function int batch_convert_dialog_event(zdialog *zd, cchar *event) { using namespace batch_convert; char countmess[60]; char *ploc; int ii, cc, yn, err; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"files")) // select images to convert { if (filelist) { // free prior list for (ii = 0; ii < filecount; ii++) zfree(filelist[ii]); zfree(filelist); filelist = 0; filecount = 0; } zdialog_show(zd,0); // hide parent dialog filelist = gallery_getfiles(); // get list of files to convert zdialog_show(zd,1); ii = 0; if (filelist) // count files selected for (ii = 0; filelist[ii]; ii++); filecount = ii; snprintf(countmess,60,Bfileselected,ii); // update dialog zdialog_stuff(zd,"fcount",countmess); } if (strmatch(event,"browse")) { zdialog_fetch(zd,"newloc",newloc,500); // 15.08 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 (zd->zstat != 1) return 1; // wait for [proceed] zd->zstat = 0; // dialog active until inputs OK zdialog_fetch(zd,"newname",newname,100); // 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 if (! filecount) { zmessageACK(Mwin,Bnofileselected); return 1; } strTrim2(newname); // check new name, baseseq, addseq 15.11 if (! blank_null(newname)) { err = 0; if (strlen(newname) < 2) err++; // disallow tiny new name if (! strstr(newname,"[oldname]") && ! strchr(newname,'#')) err++; // must use old name or sequence number if (strchr(newname,'#')) { // if '#' present, if (baseseq < 1 || addseq < 1) err++; } // baseseq and addseq present else if (baseseq || addseq) err++; // if no '#' then no sequence numbers if (err) { zmessageACK(Mwin,ZTX("new name/base/adder unreasonable" // 15.11 "\n e.g. newname ### 100 10" "\n or ... [oldname] ...")); 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; } /** 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 Delete Originals 6 PROCEED? 7 **/ char mess0[60], mess1[100], mess2[60], mess3[60], mess4[550], mess5[80], mess6[60], mess7[40]; char warnmess[800]; *mess0 = *mess1 = *mess2 = *mess3 = *mess4 = *mess5 = *mess6 = *mess7 = 0; snprintf(mess0,60,ZTX("Convert %d image files"),filecount); 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 (Fdelete) snprintf(mess6,60,"\n %s",ZTX("Delete Originals")); snprintf(mess7,40,"\n\n%s",ZTX("PROCEED?")); snprintf(warnmess,800,"%s %s %s %s %s %s %s %s",mess0,mess1,mess2,mess3,mess4,mess5,mess6,mess7); 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; int bup_filecount; 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, jj, err, bpc; char orientation; PXM *pxmin, *pxmout; cchar *exifkey[1] = { exif_orientation_key }; cchar *exifdata[1]; char **allfiles; int NAF; 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 15.03 m_viewmode(0,"F"); gallery_monitor("stop"); // stop tracking gallery updates write_popup_text("open","Processing files",500,200,Mwin); // status monitor popup window if (bup_allfiles) // "survey all files" selected 15.07 { bup_filelist = (char **) zmalloc(maximages * sizeof(char *)); bup_filecount = 0; for (ii = 0; ii < Ntopdirks; ii++) { err = find_imagefiles(topdirks[ii],allfiles,NAF,0); if (err) { write_popup_text("write","find_imagefiles() failure"); goto cleanup; } if (bup_filecount + NAF >= maximages) { write_popup_text("write","exceed max images: %d",maximages); goto cleanup; } for (jj = 0; jj < NAF; jj++) bup_filelist[bup_filecount+jj] = allfiles[jj]; bup_filecount += NAF; } } for (ii = 0; ii < bup_filecount; ii++) // loop selected files { infile = bup_filelist[ii]; // input file write_popup_text("write",infile); // log each output file zmainloop(); 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) { write_popup_text("write",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. 15.03 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) { write_popup_text("write","*** upright failed"); zfree(tempfile); continue; } exifdata[0] = ""; // remove exif:orientation exif_copy(infile,tempfile,exifkey,exifdata,1); // (set = 1 does not work) err = shell_ack("cp -p \"%s\" \"%s\" ",tempfile,infile); // copy temp file to input file if (err) write_popup_text("write",wstrerror(err)); remove(tempfile); // remove tempfile zfree(tempfile); if (! err) { load_filemeta(infile); // update image index update_image_index(infile); write_popup_text("write"," uprighted"); } } write_popup_text("write","COMPLETED"); if (navi::gallerytype == 1) // refresh gallery 15.03 gallery(0,"init"); gallery_monitor("start"); // resume tracking gallery updates 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[50]; int ii; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"files")) // select images to convert { if (bup_filelist) { // free prior list for (ii = 0; ii < bup_filecount; ii++) zfree(bup_filelist[ii]); zfree(bup_filelist); bup_filelist = 0; bup_filecount = 0; } zdialog_show(zd,0); // hide parent dialog bup_filelist = gallery_getfiles(); // get list of files to convert zdialog_show(zd,1); ii = 0; if (bup_filelist) // count files selected for (ii = 0; bup_filelist[ii]; ii++); bup_filecount = ii; snprintf(countmess,50,Bfileselected,ii); // update dialog zdialog_stuff(zd,"fcount",countmess); zdialog_stuff(zd,"allfiles",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. char **bdt_filelist; int bdt_filecount; int bdt_option; // 1/2 = delete/trash void m_batch_deltrash(GtkWidget *, cchar *) // 15.07 { 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); bdt_filelist = 0; bdt_filecount = 0; 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 (! bdt_filecount) goto cleanup; free_resources(); // no curr. file m_viewmode(0,"F"); gallery_monitor("stop"); // stop tracking gallery updates write_popup_text("open","Processing files",500,200,Mwin); // status monitor popup window for (ii = 0; ii < bdt_filecount; ii++) // loop selected files { file = bdt_filelist[ii]; // log each file write_popup_text("write",file); zmainloop(); err = stat(file,&statb); // file exists? if (err || ! S_ISREG(statb.st_mode)) { write_popup_text("write","file not found"); continue; } if (bdt_option == 1) { // delete file err = remove(file); if (err) { write_popup_text("write",wstrerror(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) { write_popup_text("write",gerror->message); continue; } } delete_image_index(file); // delete file in image index } write_popup_text("write","COMPLETED"); cleanup: if (bdt_filecount) { // free memory for (ii = 0; ii < bdt_filecount; ii++) zfree(bdt_filelist[ii]); zfree(bdt_filelist); bdt_filelist = 0; bdt_filecount = 0; } if (navi::gallerytype == 1) // refresh gallery 15.03 gallery(0,"init"); gallery_monitor("start"); // resume tracking gallery updates Fblock = 0; return; } // dialog event and completion callback function int batch_deltrash_dialog_event(zdialog *zd, cchar *event) { char countmess[50]; int ii; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"files")) // select images to convert { if (bdt_filelist) { // free prior list for (ii = 0; ii < bdt_filecount; ii++) zfree(bdt_filelist[ii]); zfree(bdt_filelist); bdt_filelist = 0; bdt_filecount = 0; } zdialog_show(zd,0); // hide parent dialog bdt_filelist = gallery_getfiles(); // get list of files to convert zdialog_show(zd,1); ii = 0; if (bdt_filelist) // count files selected for (ii = 0; bdt_filelist[ii]; ii++); bdt_filecount = ii; snprintf(countmess,50,Bfileselected,ii); // 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 (! bdt_filecount) { // nothing selected zmessageACK(Mwin,Bnofileselected); zd->zstat = 0; // keep dialog active } return 1; } /**************************************************************************/ // convert multiple RAW files to tiff, jpeg, or png using DCraw namespace batch_dcraw { char location[400]; char **filelist; int filecount; char dcraw_command[100] = ""; cchar *filetype; int bpc; float resize; int Fsharpen; int amount, thresh; }; void m_batch_dcraw(GtkWidget *, cchar *menu) { using namespace batch_dcraw; int batch_dcraw_dialog_event(zdialog *zd, cchar *event); zdialog *zd; cchar *title = ZTX("Batch Convert RAW Files (DCraw)"); char *tiffile, *infile, *outfile, *pp; int zstat, ii, err, ftype; int cc, ww2, hh2; PXM *pxm1, *pxm2; F1_help_topic = "batch_raw"; if (! Fdcraw) { zmessageACK(Mwin,ZTX("DCraw not installed")); return; } if (checkpend("all")) return; // check nothing pending Fblock = 1; /*** _____________________________________________________________________________ | [x] [-] [_] Batch Convert RAW Files (DCraw) | | | | [Select Files] N 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 [___] | | --------------------------------------------------------------------------- | | white balance (o) camera (o) fixed (o) calculated | | interpolation (o) bilinear (o) VNG (o) PPG (o) AHD | | color space (o) raw (o) sRGB (o) Adobe (o) wide (o) Kodak (o) XYZ | | gamma curve (o) default (o) sRGB (o) linear | | [ dcraw -T -6 -w -q 0 -o 1 -g 2.222 4.5 ] [defaults] | | | | [proceed] [cancel] | |_____________________________________________________________________________| ***/ zd = zdialog_new(title,Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); 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"); zdialog_add_widget(zd,"label","labout","hbout",ZTX("output location"),"space=5"); zdialog_add_widget(zd,"entry","location","hbout",0,"space=5|expand"); zdialog_add_widget(zd,"button","browse","hbout",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hb2","dialog"); 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"); 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"); 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,"spin","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,"spin","thresh","hbsharp","0|100|1|20"); zdialog_add_widget(zd,"hsep","hsep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbvb","dialog"); zdialog_add_widget(zd,"vbox","vb1","hbvb",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb2","hbvb",0,"homog"); zdialog_add_widget(zd,"label","labwbal","vb1",ZTX("white balance")); zdialog_add_widget(zd,"label","labinterp","vb1",ZTX("interpolation")); zdialog_add_widget(zd,"label","labcolor","vb1",ZTX("color space")); zdialog_add_widget(zd,"label","labgamma","vb1",ZTX("gamma curve")); zdialog_add_widget(zd,"hbox","hb3","vb2"); zdialog_add_widget(zd,"radio","-w","hb3",ZTX("camera"),"space=3"); zdialog_add_widget(zd,"radio","-W","hb3",ZTX("fixed"),"space=3"); zdialog_add_widget(zd,"radio","-a","hb3",ZTX("calculated"),"space=3"); zdialog_add_widget(zd,"hbox","hb4","vb2"); zdialog_add_widget(zd,"radio","-q 0","hb4","bilinear","space=3"); zdialog_add_widget(zd,"radio","-q 1","hb4","VNG","space=3"); zdialog_add_widget(zd,"radio","-q 2","hb4","PPG","space=3"); zdialog_add_widget(zd,"radio","-q 3","hb4","AHD","space=3"); zdialog_add_widget(zd,"hbox","hb5","vb2"); zdialog_add_widget(zd,"radio","-o 0","hb5","raw","space=3"); zdialog_add_widget(zd,"radio","-o 1","hb5","sRGB","space=3"); zdialog_add_widget(zd,"radio","-o 2","hb5","Adobe","space=3"); zdialog_add_widget(zd,"radio","-o 3","hb5","Wide","space=3"); zdialog_add_widget(zd,"radio","-o 4","hb5","Kodak","space=3"); zdialog_add_widget(zd,"radio","-o 5","hb5","XYZ","space=3"); zdialog_add_widget(zd,"hbox","hb6","vb2"); zdialog_add_widget(zd,"radio","-g 2.222 4.5","hb6",ZTX("default"),"space=3"); zdialog_add_widget(zd,"radio","-g 2.4 12.92","hb6","sRGB","space=3"); zdialog_add_widget(zd,"radio","-g 1.0 1.0","hb6","linear","space=3"); zdialog_add_widget(zd,"hbox","hb7","dialog",0,"space=3"); zdialog_add_widget(zd,"entry","dcraw_command","hb7",0,"space=5|expand"); zdialog_add_widget(zd,"button","defaults","hb7",ZTX("defaults"),"space=5"); filelist = 0; // set no files selected filecount = 0; *location = 0; zdialog_stuff(zd,"JPEG",1); // compensate GTK bug batch_dcraw_dialog_event(zd,"defaults"); // set defaults zdialog_restore_inputs(zd); // get prior inputs if any zdialog_run(zd,batch_dcraw_dialog_event,"save"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); if (zstat != 1) goto cleanup; if (! filecount) goto cleanup; printz("dcraw command: %s \n",dcraw_command); m_viewmode(0,"F"); gallery_monitor("stop"); // stop tracking gallery updates write_popup_text("open","Converting RAW files",500,200,Mwin); // status monitor popup window zmainloop(); rawfile = 0; for (ii = 0; ii < filecount; ii++) // loop all RAW files { rawfile = filelist[ii]; // filename.raw write_popup_text("write",rawfile); // write input raw file to log window zmainloop(); ftype = image_file_type(rawfile); if (ftype != 3) { write_popup_text("write"," *** unknown RAW file type"); continue; } err = shell_ack("%s \"%s\" ",dcraw_command,rawfile); // convert filename.raw to filename.tiff if (err) { write_popup_text("write"," *** raw conversion failed"); // conversion failed write_popup_text("write",wstrerror(err)); zmainloop(); continue; } tiffile = raw_to_tiff(rawfile); // converted file pxm1 = PXM_load(tiffile,0); // load tiff-16 output file if (! pxm1) { strcat(tiffile,"f"); // filename.tif >> filename.tiff 15.08 pxm1 = PXM_load(tiffile,0); // load tiff-16 output file } if (! pxm1) { write_popup_text("write"," *** no dcraw .tif output"); zmainloop(); zfree(tiffile); continue; } 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) { write_popup_text("write"," *** resize failed"); zmainloop(); zfree(tiffile); continue; } } if (Fsharpen) // sharpen if wanted batch_sharp_func(pxm1,amount,thresh); outfile = zstrdup(tiffile); // have filename.tiff, 16 bits/color pp = strrchr(outfile,'.'); // make *.jpg or *.png if wanted if (pp) strcpy(pp+1,filetype); if (strmatchN(filetype,"tif",3)) // output .tif file, 8 bits/color err = PXM_TIFF_save(pxm1,outfile,bpc); // or .tiff file, 16 bits/color 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 if (err) { write_popup_text("write"," *** file type conversion failed"); zmainloop(); zfree(tiffile); zfree(outfile); continue; } if (! strmatch(outfile,tiffile)) // if conversion was made, remove(tiffile); // delete tiff-16 file 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 = shell_ack("cp -p \"%s\" \"%s\" ",infile,outfile); // copy to new location if (err) write_popup_text("write",wstrerror(err)); remove(infile); // remove tempfile zfree(infile); } f_open(outfile,0,0,0); // open converted file 15.03 update_image_index(outfile); write_popup_text("write",outfile); // write output file to log window zfree(outfile); zfree(tiffile); zmainloop(); } write_popup_text("write","COMPLETED"); gallery(curr_dirk,"init"); // update gallery file list 15.03 gallery_monitor("start"); // resume tracking gallery updates cleanup: // clean up and return if (filecount) { for (ii = 0; ii < filecount; ii++) // free memory zfree(filelist[ii]); zfree(filelist); filelist = 0; filecount = 0; } rawfile = 0; Fblock = 0; return; } // dialog event and completion callback function int batch_dcraw_dialog_event(zdialog *zd, cchar *event) { using namespace batch_dcraw; int ii, Fupdate = 0, err, cc; char countmess[50], *ploc; static cchar *wbal = 0, *intp = 0, *color = 0, *gamma = 0; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (! wbal) { // get current options if not already wbal = "-w"; zdialog_fetch(zd,"-W",ii); if (ii) wbal = "-W"; zdialog_fetch(zd,"-a",ii); if (ii) wbal = "-a"; Fupdate++; } if (! intp) { intp = "-q 0"; zdialog_fetch(zd,"-q 1",ii); if (ii) intp = "-q 1"; zdialog_fetch(zd,"-q 2",ii); if (ii) intp = "-q 2"; zdialog_fetch(zd,"-q 3",ii); if (ii) intp = "-q 3"; Fupdate++; } if (! color) { color = "-o 1"; zdialog_fetch(zd,"-o 0",ii); if (ii) color = "-o 0"; zdialog_fetch(zd,"-o 2",ii); if (ii) color = "-o 2"; zdialog_fetch(zd,"-o 3",ii); if (ii) color = "-o 3"; zdialog_fetch(zd,"-o 4",ii); if (ii) color = "-o 4"; zdialog_fetch(zd,"-o 5",ii); if (ii) color = "-o 5"; Fupdate++; } if (! gamma) { gamma = "-g 2.222 4.5"; zdialog_fetch(zd,"-g 2.4 12.92",ii); if (ii) gamma = "-g 2.4 12.92"; zdialog_fetch(zd,"-g 1.0 1.0",ii); if (ii) gamma = "-g 1.0 1.0"; Fupdate++; } if (strmatch(event,"files")) // select images to convert { if (filecount) { // free prior list for (ii = 0; ii < filecount; ii++) zfree(filelist[ii]); zfree(filelist); filelist = 0; filecount = 0; } zdialog_show(zd,0); // hide parent dialog filelist = gallery_getfiles(); // get list of files to convert zdialog_show(zd,1); ii = 0; if (filelist) // count files in list for (ii = 0; filelist[ii]; ii++); filecount = ii; snprintf(countmess,50,Bfileselected,ii); // stuff count into dialog zdialog_stuff(zd,"fcount",countmess); } if (strmatch(event,"browse")) { zdialog_fetch(zd,"location",location,400); // 15.08 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 (strmatch(event,"defaults")) // default all parameters { zdialog_stuff(zd,"JPEG",0); zdialog_stuff(zd,"PNG-8",0); zdialog_stuff(zd,"PNG-16",0); zdialog_stuff(zd,"TIFF-8",0); zdialog_stuff(zd,"TIFF-16",1); // output = TIFF-16 zdialog_stuff(zd,"-w",1); // color balance = camera value zdialog_stuff(zd,"-W",0); zdialog_stuff(zd,"-a",0); wbal = "-w"; zdialog_stuff(zd,"-q 0",1); // interpolation = bilinear zdialog_stuff(zd,"-q 1",0); zdialog_stuff(zd,"-q 2",0); zdialog_stuff(zd,"-q 3",0); intp = "-q 0"; zdialog_stuff(zd,"-o 0",0); zdialog_stuff(zd,"-o 1",1); // color space = sRGB zdialog_stuff(zd,"-o 2",0); zdialog_stuff(zd,"-o 3",0); zdialog_stuff(zd,"-o 4",0); zdialog_stuff(zd,"-o 5",0); color = "-o 1"; zdialog_stuff(zd,"-g 2.222 4.5",1); // gamma = default zdialog_stuff(zd,"-g 2.4 12.92",0); zdialog_stuff(zd,"-g 1.0 1.0",0); gamma = "-g 2.222 4.5"; Fupdate++; } if (strstr("-w -W -a",event)) { // event is one of: -w -W -a zdialog_fetch(zd,event,ii); if (ii) { wbal = event; Fupdate++; } } if (strstr(event,"-q")) { // event is: -q N zdialog_fetch(zd,event,ii); if (ii) { intp = event; Fupdate++; } } if (strstr(event,"-o")) { // event is: -o N zdialog_fetch(zd,event,ii); if (ii) { color = event; Fupdate++; } } if (strstr(event,"-g")) { // event is: -g N1 N2 zdialog_fetch(zd,event,ii); if (ii) { gamma = event; Fupdate++; } } if (Fupdate || ! *dcraw_command) // initialize or update dcraw command { snprintf(dcraw_command,100,"dcraw -T -6 %s %s %s %s",wbal,intp,color,gamma); zdialog_stuff(zd,"dcraw_command",dcraw_command); } if (zd->zstat != 1) return 0; // wait for [proceed] zd->zstat = 0; // keep dialog active until inputs OK if (! filecount) { 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 = "tiff"; bpc = 16; } 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; } /**************************************************************************/ // convert multiple RAW files to tiff, jpeg, or png using Raw Therapee namespace batch_rawtherapee { char location[400]; char **filelist; int filecount; char rawtherapee_command[100] = ""; cchar *filetype; int bpc; float resize; int Fsharpen; int amount, thresh; }; void m_batch_rawtherapee(GtkWidget *, cchar *menu) { using namespace batch_rawtherapee; int batch_rawtherapee_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; int cc, ww2, hh2; PXM *pxm1, *pxm2; F1_help_topic = "batch_raw"; if (! Frawtherapee) { zmessageACK(Mwin,ZTX("Raw Therapee not installed")); return; } if (checkpend("all")) return; // check nothing pending Fblock = 1; /*** _____________________________________________________________________________ | [x] [-] [_] Batch Convert RAW Files (Raw Therapee) | | | | [Select Files] N files selected | | output location [_________________________________________________] | | 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","hb1","dialog",0,"space=3"); 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"); zdialog_add_widget(zd,"label","labout","hbout",ZTX("output location"),"space=5"); zdialog_add_widget(zd,"entry","location","hbout",0,"space=5|expand"); zdialog_add_widget(zd,"button","browse","hbout",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hb2","dialog"); 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"); 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"); 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,"spin","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,"spin","thresh","hbsharp","0|100|1|20"); filelist = 0; // set no files selected filecount = 0; *location = 0; zdialog_stuff(zd,"JPEG",1); // compensate GTK bug zdialog_restore_inputs(zd); // get prior inputs if any zdialog_run(zd,batch_rawtherapee_dialog_event,"save"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); if (zstat != 1) goto cleanup; if (! filecount) goto cleanup; m_viewmode(0,"F"); gallery_monitor("stop"); // stop tracking gallery updates write_popup_text("open","Converting RAW files",500,200,Mwin); // status monitor popup window zmainloop(); rawfile = 0; for (ii = 0; ii < filecount; ii++) // loop all RAW files { rawfile = filelist[ii]; // filename.raw write_popup_text("write",rawfile); // write input raw file to log window zmainloop(); ftype = image_file_type(rawfile); if (ftype != 3) { write_popup_text("write"," *** unknown RAW file type"); continue; } snprintf(rawtherapee_command,100,"rawtherapee -t -Y -c "); err = shell_ack("%s \"%s\" ",rawtherapee_command,rawfile); // convert filename.raw to filename.tif if (err) { write_popup_text("write"," *** raw conversion failed"); // conversion failed write_popup_text("write",wstrerror(err)); zmainloop(); continue; } tiffile = raw_to_tiff(rawfile); // rawfilename.tif pxm1 = PXM_load(tiffile,0); // load tiff-16 output file if (! pxm1) { strcat(tiffile,"f"); // filename.tif >> filename.tiff 15.08 pxm1 = PXM_load(tiffile,0); // load tiff-16 output file } if (! pxm1) { write_popup_text("write"," *** no Raw Therapee .tif output"); zmainloop(); zfree(tiffile); continue; } 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) { write_popup_text("write"," *** resize failed"); zmainloop(); zfree(tiffile); continue; } } if (Fsharpen) // sharpen if wanted batch_sharp_func(pxm1,amount,thresh); outfile = zstrdup(tiffile); // have filename.tif, 16 bits/color pp = strrchr(outfile,'.'); // make *.jpg or *.png if wanted if (pp) strcpy(pp+1,filetype); if (strmatchN(filetype,"tif",3)) // output .tif file, 8 bits/color err = PXM_TIFF_save(pxm1,outfile,bpc); // or .tiff file, 16 bits/color 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 if (err) { write_popup_text("write"," *** file type conversion failed"); zmainloop(); zfree(tiffile); zfree(outfile); continue; } if (! strmatch(outfile,tiffile)) // if conversion was made, remove(tiffile); // delete tiff-16 file 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 = shell_ack("cp -p \"%s\" \"%s\" ",infile,outfile); // copy to new location if (err) write_popup_text("write",wstrerror(err)); remove(infile); // remove tempfile zfree(infile); } f_open(outfile,0,0,0); // open converted file 15.03 update_image_index(outfile); write_popup_text("write",outfile); // write output file to log window zfree(outfile); zfree(tiffile); zmainloop(); } write_popup_text("write","COMPLETED"); gallery(curr_dirk,"init"); // update gallery file list 15.03 gallery_monitor("start"); // resume tracking gallery updates cleanup: // clean up and return if (filecount) { for (ii = 0; ii < filecount; ii++) // free memory zfree(filelist[ii]); zfree(filelist); filelist = 0; filecount = 0; } rawfile = 0; Fblock = 0; return; } // dialog event and completion callback function int batch_rawtherapee_dialog_event(zdialog *zd, cchar *event) { using namespace batch_rawtherapee; int ii, err, cc; char countmess[50], *ploc; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"files")) // select images to convert { if (filecount) { // free prior list for (ii = 0; ii < filecount; ii++) zfree(filelist[ii]); zfree(filelist); filelist = 0; filecount = 0; } zdialog_show(zd,0); // hide parent dialog filelist = gallery_getfiles(); // get list of files to convert zdialog_show(zd,1); ii = 0; if (filelist) // count files in list for (ii = 0; filelist[ii]; ii++); filecount = ii; snprintf(countmess,50,Bfileselected,ii); // stuff count into dialog zdialog_stuff(zd,"fcount",countmess); } if (strmatch(event,"browse")) { zdialog_fetch(zd,"location",location,400); // 15.08 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 if (! filecount) { 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 = "tiff"; bpc = 16; } 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_run(); char *scriptfile = 0; // script filespec FILE *fid = 0; // script file FID } // menu function void m_scriptfiles(GtkWidget *, cchar *menu) // 15.10 { using namespace script; F1_help_topic = "script_files"; /*** _________________________________________ | Script Files | | | | [start] begin making a script file | | [close] finish making a script file | | [run] execute a script file | | | | [cancel] | |_________________________________________| ***/ zdialog *zd = zdialog_new("Script Files",Mwin,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"button","start","hb1",ZTX("start"),"space=5"); zdialog_add_widget(zd,"label","labstart","hb1",ZTX("begin making a script file")); zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"button","close","hb2",ZTX("close"),"space=5"); zdialog_add_widget(zd,"label","labclose","hb2",ZTX("finish making a script file")); zdialog_add_widget(zd,"hbox","hb3","dialog"); zdialog_add_widget(zd,"button","run","hb3",ZTX("run"),"space=5"); zdialog_add_widget(zd,"label","labrun","hb3",ZTX("execute a script file")); zdialog_run(zd,dialog_event); return; } // dialog event and completion function int script::dialog_event(zdialog *zd, cchar *event) { using namespace script; F1_help_topic = "script_files"; if (strmatch(event,"escape")) zd->zstat = 1; // escape = cancel if (zd->zstat) { // cancel zdialog_free(zd); if (fid) fclose(fid); fid = 0; Fscriptbuild = 0; return 1; } if (strmatch(event,"start")) { script_start(); zdialog_free(zd); } if (strmatch(event,"close")) script_close(); if (strmatch(event,"run")) { zdialog_free(zd); 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 *pp; if (checkpend("all")) return 0; if (Fscriptbuild) { zmessageACK(Mwin,ZTX("script already started")); return 0; } pp = zgetfile(ZTX("open new script file"),MWIN,"save",edit_scripts_dirk,1); if (! pp) return 0; if (scriptfile) zfree(scriptfile); scriptfile = pp; fid = fopen(scriptfile,"w"); if (! fid) { zmessageACK(Mwin,strerror(errno)); return 0; } pp = strrchr(scriptfile,'/'); fprintf(fid,"script file: %s\n",pp+1); Fscriptbuild = 1; 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; } // execute a script file // returns 0 if OK, +N if error int script::script_run() { using namespace script; char **filelist = 0, *imagefile, *pp; char scriptname[100], buff[100], menuname[40]; char *newfilevers; int ii, jj, err; int retstat = 1; // failure if (Fscriptbuild) { zmessageACK(Mwin,ZTX("script file is not closed")); return 1; } zmessageACK(Mwin,ZTX("select script file to run")); pp = zgetfile(ZTX("open script file"),MWIN,"file",edit_scripts_dirk,1); if (! pp) goto cleanup; if (scriptfile) zfree(scriptfile); scriptfile = pp; pp = strrchr(scriptfile,'/'); // get file name if (! pp || strlen(pp) > 99) { zmessageACK(Mwin,ZTX("unknown script file")); goto cleanup; } strncpy0(scriptname,pp+1,100); // save script name fid = fopen(scriptfile,"r"); // open script file if (! fid) { zmessageACK(Mwin,ZTX("script file: %s \n %s"),scriptfile,strerror(errno)); goto cleanup; } pp = fgets_trim(buff,100,fid,1); // read "script file: scriptname" if (! pp) goto badfile; if (! strmatchN("script file: ",pp,13)) goto badfile; if (! strmatch(pp+13,scriptname)) goto badfile; fclose(fid); zmessageACK(Mwin,ZTX("select image files to be processed")); filelist = gallery_getfiles(); // get list of files to edit if (! filelist) goto cleanup; if (checkpend("all")) return 0; for (ii = 0; filelist[ii]; ii++) // loop image files to process { imagefile = filelist[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"),scriptname); cleanup: if (fid) fclose(fid); fid = 0; if (filelist) { for (ii = 0; filelist[ii]; ii++) // free memory zfree(filelist[ii]); zfree(filelist); } return retstat; } /**************************************************************************/ // burn images to CD/DVD void m_burn(GtkWidget *, cchar *) { int ii, cc; char **filelist, *imagefile, *bcommand; if (! Fbrasero) { zmessageACK(Mwin,ZTX("Brasero not installed")); return; } if (checkpend("all")) return; // check nothing pending Fblock = 1; F1_help_topic = "burn_DVD"; filelist = gallery_getfiles(); // get list of files to burn if (! filelist) { Fblock = 0; return; } m_viewmode(0,"F"); cc = 0; for (ii = 0; filelist[ii]; ii++) // get memory for brasero command line cc += strlen(filelist[ii]) + 4; bcommand = (char *) zmalloc(cc+20); strcpy(bcommand,"brasero "); cc = strlen(bcommand); for (ii = 0; filelist[ii]; ii++) // copy files to command line { imagefile = filelist[ii]; strcpy(bcommand+cc," \""); cc += 2; strcpy(bcommand+cc,imagefile); cc += strlen(imagefile); strcpy(bcommand+cc,"\""); cc += 1; zfree(imagefile); } zfree(filelist); strcat(bcommand," &"); // brasero command in background shell_ack(bcommand); zfree(bcommand); Fblock = 0; return; } /**************************************************************************/ // 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 |-+] | | Images: 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")); zdialog_add_widget(zd,"spin","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")); zdialog_add_widget(zd,"spin","pixdiff","hbdiff","1|20|1|2","space=3"); zdialog_add_widget(zd,"hbox","hbsum","dialog"); zdialog_add_widget(zd,"label","labsum","hbsum",ZTX("pixel count")); zdialog_add_widget(zd,"spin","pixcount","hbsum","1|999|1|10","space=3"); zdialog_add_widget(zd,"hbox","hbcount","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labcount1","hbcount",ZTX("Images:")); zdialog_add_widget(zd,"label","labcount2","hbcount",ZTX("searching ...")); 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=5"); zdialog_add_widget(zd,"hbox","hbfile","dialog"); zdialog_add_widget(zd,"label","labfile","hbfile"," ","space=5"); zdialog_restore_inputs(zd); zdialog_resize(zd,300,0); zdialog_run(zd,duplicates_dialog_event); err = find_imagefiles(thumbdirk,files,Nfiles,1); // get all thumbnail files 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 15.02 { // 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); 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 = 2; // generate gallery of duplicate images gallery(gfile,"initF"); m_viewmode(0,"G"); gallery(0,"paint",0); // position at top 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: zmessLogACK(Mwin,"file error: %s",strerror(errno)); goto cleanup; } // dialog event and completion function int duplicates_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (! zd->zstat) return 1; // wait for user input if (zd->zstat != 1) Fkillfunc = 1; // [cancel] or [x] return 1; // [proceed] } fotoxx-15.11.1/locales/0000755000175000017500000000000012616075370013300 5ustar micomicofotoxx-15.11.1/locales/translate-en.po0000664000175000017500000030732312616075370016247 0ustar micomico# English translations for home package. # Copyright (C) 2015 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: 2015-11-02 21:36+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:78 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:80 msgid "Drag album thumbnail to new position." msgstr "Drag album thumbnail to new position." #: f.albums.cc:109 f.widgets.cc:548 msgid "Manage Albums" msgstr "Manage Albums" #: f.albums.cc:120 f.albums.cc:258 msgid "Create or replace an album" msgstr "Create or replace an album" #: f.albums.cc:122 msgid "Album to view or edit" msgstr "Album to view or edit" #: f.albums.cc:124 msgid "Select images, add to cache" msgstr "Select images, add to cache" #: f.albums.cc:126 msgid "Clear image cache" msgstr "Clear image cache" #: f.albums.cc:128 f.albums.cc:150 msgid "Delete an album" msgstr "Delete an album" #: f.albums.cc:149 msgid "Choose Album" msgstr "Choose Album" #: f.albums.cc:151 #, c-format msgid "Image cache has %d images" msgstr "Image cache has %d images" #: f.albums.cc:152 msgid "cache added to empty album" msgstr "cache added to empty album" #: f.albums.cc:207 #, c-format msgid "delete %s ?" msgstr "delete %s ?" #: f.albums.cc:242 #, c-format msgid "fill from image cache (%d images)" msgstr "fill from image cache (%d images)" #: f.albums.cc:260 f.albums.cc:297 msgid "Album Name" msgstr "Album Name" #: f.albums.cc:266 msgid "make an initially empty album" msgstr "make an initially empty album" #: f.albums.cc:268 msgid "fill from current gallery" msgstr "fill from current gallery" #: f.albums.cc:311 msgid "enter an album name" msgstr "enter an album name" #: f.albums.cc:338 msgid "gallery is empty" msgstr "gallery is empty" #: f.albums.cc:365 msgid "new album created" msgstr "new album created" #: f.albums.cc:1099 msgid "Press ESC to exit slide show" msgstr "Press ESC to exit slide show" #: f.albums.cc:1101 msgid "instant" msgstr "instant" #: f.albums.cc:1102 msgid "fade-in" msgstr "fade-in" #: f.albums.cc:1103 msgid "roll-right" msgstr "roll-right" #: f.albums.cc:1104 msgid "roll-down" msgstr "roll-down" #: f.albums.cc:1105 msgid "venetian" msgstr "venetian" #: f.albums.cc:1106 msgid "grate" msgstr "grate" #: f.albums.cc:1107 msgid "rectangle" msgstr "rectangle" #: f.albums.cc:1108 msgid "implode" msgstr "implode" #: f.albums.cc:1109 msgid "explode" msgstr "explode" #: f.albums.cc:1110 msgid "radar" msgstr "radar" #: f.albums.cc:1111 msgid "Japanese-fan" msgstr "Japanese-fan" #: f.albums.cc:1112 msgid "jaws" msgstr "jaws" #: f.albums.cc:1113 msgid "ellipse" msgstr "ellipse" #: f.albums.cc:1114 msgid "raindrops" msgstr "raindrops" #: f.albums.cc:1115 msgid "doubledoor" msgstr "doubledoor" #: f.albums.cc:1116 msgid "rotate" msgstr "rotate" #: f.albums.cc:1117 msgid "fallover" msgstr "fallover" #: f.albums.cc:1118 msgid "sphereoid" msgstr "sphereoid" #: f.albums.cc:1139 f.widgets.cc:549 msgid "Slide Show" msgstr "Slide Show" #: f.albums.cc:1150 msgid "Clip Limit" msgstr "Clip Limit" #: f.albums.cc:1154 msgid "Music File" msgstr "Music File" #: f.albums.cc:1159 msgid "Full Screen" msgstr "Full Screen" #: f.albums.cc:1160 msgid "Auto-replay" msgstr "Auto-replay" #: f.albums.cc:1166 msgid "Customize:" msgstr "Customize:" #: f.albums.cc:1167 msgid "transitions" msgstr "transitions" #: f.albums.cc:1168 msgid "image files" msgstr "image files" #: f.albums.cc:1173 f.albums.cc:1289 #, c-format msgid "%d images" msgstr "%d images" #: f.albums.cc:1195 f.albums.cc:1259 f.albums.cc:1349 f.albums.cc:1462 #: f.albums.cc:1655 f.albums.cc:1739 f.albums.cc:1756 f.albums.cc:1977 msgid "invalid album" msgstr "invalid album" #: f.albums.cc:1273 msgid "open album" msgstr "open album" #: f.albums.cc:1305 msgid "Select music file" msgstr "Select music file" #: f.albums.cc:1329 msgid "select random (if 5+ enabled)" msgstr "select random (if 5+ enabled)" #: f.albums.cc:1353 msgid "Transition Preferences" msgstr "Transition Preferences" #: f.albums.cc:1362 msgid "transition" msgstr "transition" #: f.albums.cc:1363 msgid "enabled" msgstr "enabled" #: f.albums.cc:1364 msgid "slowdown" msgstr "slowdown" #: f.albums.cc:1365 msgid "preference" msgstr "preference" #: f.albums.cc:1468 msgid "Image Preferences" msgstr "Image Preferences" #: f.albums.cc:1472 f.file.cc:1159 f.file.cc:1400 f.file.cc:1608 f.meta.cc:437 msgid "Image File:" msgstr "Image File:" #: f.albums.cc:1476 msgid "Play tone when image shows" msgstr "Play tone when image shows" #: f.albums.cc:1479 msgid "Show image caption" msgstr "Show image caption" #: f.albums.cc:1484 msgid "Show image comments" msgstr "Show image comments" #: f.albums.cc:1489 msgid "Wait before zoom" msgstr "Wait before zoom" #: f.albums.cc:1494 msgid "Zoom type:" msgstr "Zoom type:" #: f.albums.cc:1495 msgid "none" msgstr "none" #: f.albums.cc:1496 msgid "zoom-in" msgstr "zoom-in" #: f.albums.cc:1497 msgid "zoom-out" msgstr "zoom-out" #: f.albums.cc:1500 msgid "Zoom size (x)" msgstr "Zoom size (x)" #: f.albums.cc:1503 msgid "Steps" msgstr "Steps" #: f.albums.cc:1507 msgid "Zoom image location:" msgstr "Zoom image location:" #: f.albums.cc:1515 msgid "Wait after zoom" msgstr "Wait after zoom" #: f.albums.cc:1520 msgid "Transition to next image" msgstr "Transition to next image" #: f.albums.cc:1523 f.albums.cc:1625 msgid "next" msgstr "next" #: f.albums.cc:1962 f.albums.cc:2120 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "file format error: \n" " %s" #: f.area.cc:79 msgid "Select Area for Edits" msgstr "Select Area for Edits" #: f.area.cc:80 f.edit.cc:5927 msgid "Press F1 for help" msgstr "Press F1 for help" #: f.area.cc:89 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Select Area not supported \n" "by this edit function" #: f.area.cc:119 msgid "select rectangle" msgstr "select rectangle" #: f.area.cc:120 msgid "select ellipse" msgstr "select ellipse" #: f.area.cc:123 msgid "freehand draw" msgstr "freehand draw" #: f.area.cc:124 msgid "follow edge" msgstr "follow edge" #: f.area.cc:125 msgid "draw/replace" msgstr "draw/replace" #: f.area.cc:128 msgid "select area within mouse" msgstr "select area within mouse" #: f.area.cc:131 msgid "select one matching color within mouse" msgstr "select one matching color within mouse" #: f.area.cc:135 msgid "select all matching colors within mouse" msgstr "select all matching colors within mouse" #: f.area.cc:139 f.edit.cc:5972 msgid "mouse radius" msgstr "mouse radius" #: f.area.cc:142 msgid "match level %" msgstr "match level %" #: f.area.cc:147 msgid "search range" msgstr "search range" #: f.area.cc:149 msgid "firewall" msgstr "firewall" #: f.area.cc:152 msgid "Area Edge Blend Width" msgstr "Area Edge Blend Width" #: f.area.cc:155 msgid "Edge Creep" msgstr "Edge Creep" #: f.area.cc:160 msgid "Line Color:" msgstr "Line Color:" #: f.area.cc:180 f.area.cc:181 msgid "area edits fade away within edge distance" msgstr "area edits fade away within edge distance" #: f.area.cc:360 f.area.cc:543 #, c-format msgid "exceed %d edits" msgstr "exceed %d edits" #: f.area.cc:1364 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:1392 msgid "finish area" msgstr "finish area" #: f.area.cc:1400 msgid "extend to corner:" msgstr "extend to corner:" #: f.area.cc:1441 msgid "searching" msgstr "searching" #: f.area.cc:1472 msgid "outline has a gap" msgstr "outline has a gap" #: f.area.cc:1476 msgid "success" msgstr "success" #: f.area.cc:1613 f.area.cc:1640 #, c-format msgid "found %d pixels" msgstr "found %d pixels" #: f.area.cc:1935 f.area.cc:1952 f.area.cc:1972 f.area.cc:2287 f.area.cc:2363 #: f.area.cc:2520 msgid "the area is not finished" msgstr "the area is not finished" #: f.area.cc:2061 msgid "Edge calculation in progress" msgstr "Edge calculation in progress" #: f.area.cc:2070 msgid "Area Edge Calc" msgstr "Area Edge Calc" #: f.area.cc:2471 msgid "load area from a file" msgstr "load area from a file" #: f.area.cc:2554 msgid "save area as a PNG file" msgstr "save area as a PNG file" #: f.area.cc:2582 msgid "position with mouse click/drag" msgstr "position with mouse click/drag" #: f.area.cc:2638 msgid "Paste Image" msgstr "Paste Image" #: f.area.cc:2643 f.batch.cc:1169 f.batch.cc:1660 msgid "resize" msgstr "resize" #: f.batch.cc:107 f.widgets.cc:531 msgid "Batch Convert" msgstr "Batch Convert" #: f.batch.cc:114 f.file.cc:942 msgid "New Name" msgstr "New Name" #: f.batch.cc:118 msgid "Sequence Numbers" msgstr "Sequence Numbers" #: f.batch.cc:120 msgid "base" msgstr "base" #: f.batch.cc:122 msgid "adder" msgstr "adder" #: f.batch.cc:127 msgid "New Location" msgstr "New Location" #: f.batch.cc:132 msgid "New File Type" msgstr "New File Type" #: f.batch.cc:136 f.batch.cc:144 f.repair.cc:3861 msgid "no change" msgstr "no change" #: f.batch.cc:139 msgid "max. Width" msgstr "max. Width" #: f.batch.cc:147 f.batch.cc:565 msgid "Delete Originals" msgstr "Delete Originals" #: f.batch.cc:148 f.batch.cc:562 msgid "Copy Metadata" msgstr "Copy Metadata" #: f.batch.cc:149 f.batch.cc:563 f.edit.cc:1413 f.widgets.cc:438 #: f.widgets.cc:684 msgid "Upright" msgstr "Upright" #: f.batch.cc:152 f.batch.cc:564 f.batch.cc:1178 f.batch.cc:1669 #: f.repair.cc:118 f.widgets.cc:454 msgid "Sharpen" msgstr "Sharpen" #: f.batch.cc:154 f.batch.cc:1180 f.batch.cc:1671 msgid "amount" msgstr "amount" #: f.batch.cc:157 f.batch.cc:1183 f.batch.cc:1674 msgid "threshold" msgstr "threshold" #: f.batch.cc:280 msgid "file type not supported" msgstr "file type not supported" #: f.batch.cc:344 msgid "cannot create new file" msgstr "cannot create new file" #: f.batch.cc:391 msgid "updating albums ..." msgstr "updating albums ..." #: f.batch.cc:464 f.batch.cc:1456 f.batch.cc:1862 f.file.cc:1208 msgid "Select directory" msgstr "Select directory" #: f.batch.cc:513 msgid "" "new name/base/adder unreasonable\n" " e.g. newname ### 100 10\n" " or ... [oldname] ..." msgstr "" "new name/base/adder unreasonable\n" " e.g. newname ### 100 10\n" " or ... [oldname] ..." #: f.batch.cc:537 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "max. size %d x %d is not reasonable" #: f.batch.cc:556 #, c-format msgid "Convert %d image files" msgstr "Convert %d image files" #: f.batch.cc:557 msgid "Rename to" msgstr "Rename to" #: f.batch.cc:558 msgid "Convert to" msgstr "Convert to" #: f.batch.cc:559 msgid "Resize within" msgstr "Resize within" #: f.batch.cc:560 msgid "Output to" msgstr "Output to" #: f.batch.cc:566 msgid "PROCEED?" msgstr "PROCEED?" #: f.batch.cc:720 f.widgets.cc:532 msgid "Batch Upright" msgstr "Batch Upright" #: f.batch.cc:726 msgid "Survey all files" msgstr "Survey all files" #: f.batch.cc:780 msgid "file cannot be read" msgstr "file cannot be read" #: f.batch.cc:898 msgid "cannot select both options" msgstr "cannot select both options" #: f.batch.cc:942 f.widgets.cc:533 msgid "Batch Delete/Trash" msgstr "Batch Delete/Trash" #: f.batch.cc:947 msgid "delete" msgstr "delete" #: f.batch.cc:950 msgid "trash" msgstr "trash" #: f.batch.cc:1112 msgid "Batch Convert RAW Files (DCraw)" msgstr "Batch Convert RAW Files (DCraw)" #: f.batch.cc:1121 msgid "DCraw not installed" msgstr "DCraw not installed" #: f.batch.cc:1156 f.batch.cc:1647 msgid "output location" msgstr "output location" #: f.batch.cc:1161 f.batch.cc:1652 msgid "output file type" msgstr "output file type" #: f.batch.cc:1192 msgid "white balance" msgstr "white balance" #: f.batch.cc:1193 msgid "interpolation" msgstr "interpolation" #: f.batch.cc:1194 msgid "color space" msgstr "color space" #: f.batch.cc:1195 msgid "gamma curve" msgstr "gamma curve" #: f.batch.cc:1198 msgid "camera" msgstr "camera" #: f.batch.cc:1199 msgid "fixed" msgstr "fixed" #: f.batch.cc:1200 msgid "calculated" msgstr "calculated" #: f.batch.cc:1217 msgid "default" msgstr "default" #: f.batch.cc:1223 msgid "defaults" msgstr "defaults" #: f.batch.cc:1610 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Batch Convert RAW Files (Raw Therapee)" #: f.batch.cc:1619 f.file.cc:428 msgid "Raw Therapee not installed" msgstr "Raw Therapee not installed" #: f.batch.cc:1959 msgid "start" msgstr "start" #: f.batch.cc:1960 msgid "begin making a script file" msgstr "begin making a script file" #: f.batch.cc:1962 msgid "close" msgstr "close" #: f.batch.cc:1963 msgid "finish making a script file" msgstr "finish making a script file" #: f.batch.cc:1965 msgid "run" msgstr "run" #: f.batch.cc:1966 msgid "execute a script file" msgstr "execute a script file" #: f.batch.cc:2020 msgid "script already started" msgstr "script already started" #: f.batch.cc:2024 msgid "open new script file" msgstr "open new script file" #: f.batch.cc:2056 msgid "script file error" msgstr "script file error" #: f.batch.cc:2061 #, c-format msgid "%s added to script" msgstr "%s added to script" #: f.batch.cc:2074 msgid "no script file was started" msgstr "no script file was started" #: f.batch.cc:2082 msgid "script file closed" msgstr "script file closed" #: f.batch.cc:2101 msgid "script file is not closed" msgstr "script file is not closed" #: f.batch.cc:2105 msgid "select script file to run" msgstr "select script file to run" #: f.batch.cc:2107 msgid "open script file" msgstr "open script file" #: f.batch.cc:2114 msgid "unknown script file" msgstr "unknown script file" #: f.batch.cc:2121 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" "script file: %s \n" " %s" #: f.batch.cc:2132 msgid "select image files to be processed" msgstr "select image files to be processed" #: f.batch.cc:2144 f.batch.cc:2193 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "open failure: %s \n" " %s" #: f.batch.cc:2165 #, c-format msgid "unknown edit function: %s" msgstr "unknown edit function: %s" #: f.batch.cc:2174 #, c-format msgid "load widgets failed: %s" msgstr "load widgets failed: %s" #: f.batch.cc:2203 #, c-format msgid "script file format error: %s" msgstr "script file format error: %s" #: f.batch.cc:2229 msgid "Brasero not installed" msgstr "Brasero not installed" #: f.batch.cc:2318 f.widgets.cc:538 msgid "Find Duplicate Images" msgstr "Find Duplicate Images" #: f.batch.cc:2320 f.tools.cc:929 msgid "Thumbnail size" msgstr "Thumbnail size" #: f.batch.cc:2323 msgid "pixel difference" msgstr "pixel difference" #: f.batch.cc:2326 msgid "pixel count" msgstr "pixel count" #: f.batch.cc:2329 msgid "Images:" msgstr "Images:" #: f.batch.cc:2330 msgid "searching ..." msgstr "searching ..." #: f.batch.cc:2332 msgid "Duplicates:" msgstr "Duplicates:" #: f.batch.cc:2350 #, c-format msgid "only %d image thumbnails found" msgstr "only %d image thumbnails found" #: f.bend.cc:86 f.widgets.cc:475 msgid "Unbend" msgstr "Unbend" #: f.bend.cc:94 f.edit.cc:4267 f.effects.cc:4725 msgid "vertical" msgstr "vertical" #: f.bend.cc:95 f.edit.cc:4266 f.effects.cc:4721 msgid "horizontal" msgstr "horizontal" #: f.bend.cc:96 msgid "linear" msgstr "linear" #: f.bend.cc:99 msgid "curved" msgstr "curved" #: f.bend.cc:367 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.bend.cc:384 msgid "Perspective Correction" msgstr "Perspective Correction" #: f.bend.cc:590 msgid "must have 4 corners" msgstr "must have 4 corners" #: f.bend.cc:712 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.bend.cc:725 f.widgets.cc:477 msgid "Warp area" msgstr "Warp area" #: f.bend.cc:730 msgid "start warp" msgstr "start warp" #: f.bend.cc:788 msgid "no active Select Area" msgstr "no active Select Area" #: f.bend.cc:1129 f.bend.cc:1443 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.bend.cc:1147 f.widgets.cc:478 msgid "Warp curved" msgstr "Warp curved" #: f.bend.cc:1156 f.mashup.cc:1836 msgid "warp span" msgstr "warp span" #: f.bend.cc:1461 f.widgets.cc:479 msgid "Warp linear" msgstr "Warp linear" #: f.bend.cc:1776 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.bend.cc:1792 f.widgets.cc:480 msgid "Warp affine" msgstr "Warp affine" #: f.bend.cc:2138 msgid "Flatten Book Page Photo" msgstr "Flatten Book Page Photo" #: f.bend.cc:2139 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.bend.cc:2142 msgid "Stretch curved-down surfaces:" msgstr "Stretch curved-down surfaces:" #: f.bend.cc:2197 msgid "top:" msgstr "top:" #: f.bend.cc:2200 msgid "bottom:" msgstr "bottom:" #: f.combine.cc:2108 f.combine.cc:2740 f.combine.cc:3396 f.combine.cc:3928 msgid "Select 2 to 9 files" msgstr "Select 2 to 9 files" #: f.combine.cc:2129 f.combine.cc:2761 f.combine.cc:3417 f.combine.cc:3949 msgid "Images are not all the same size" msgstr "Images are not all the same size" #: f.combine.cc:2473 msgid "Adjust Image Contributions" msgstr "Adjust Image Contributions" #: f.combine.cc:2477 msgid "dark pixels" msgstr "dark pixels" #: f.combine.cc:2479 msgid "light pixels" msgstr "light pixels" #: f.combine.cc:2481 msgid "file:" msgstr "file:" #: f.combine.cc:2997 msgid "Paint and Warp Image" msgstr "Paint and Warp Image" #: f.combine.cc:3001 f.combine.cc:3646 f.combine.cc:6237 f.effects.cc:3912 #: f.widgets.cc:567 f.widgets.cc:590 f.widgets.cc:612 msgid "Image" msgstr "Image" #: f.combine.cc:3005 msgid "paint" msgstr "paint" #: f.combine.cc:3006 msgid "warp" msgstr "warp" #: f.combine.cc:3644 msgid "Select and Paint Image" msgstr "Select and Paint Image" #: f.combine.cc:4120 msgid "Adjust Pixel Composition" msgstr "Adjust Pixel Composition" #: f.combine.cc:4122 msgid "use average" msgstr "use average" #: f.combine.cc:4123 msgid "use median" msgstr "use median" #: f.combine.cc:4125 msgid "omit low pixel" msgstr "omit low pixel" #: f.combine.cc:4126 msgid "omit high pixel" msgstr "omit high pixel" #: f.combine.cc:4388 f.combine.cc:5494 msgid "Select 2 to 4 files" msgstr "Select 2 to 4 files" #: f.combine.cc:4463 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:4465 f.combine.cc:5571 msgid "no curve (scanned image)" msgstr "no curve (scanned image)" #: f.combine.cc:4466 f.combine.cc:5572 msgid "Search for lens mm" msgstr "Search for lens mm" #: f.combine.cc:4467 f.combine.cc:5573 msgid "Save lens mm → image EXIF" msgstr "Save lens mm → image EXIF" #: f.combine.cc:4528 f.combine.cc:5634 msgid "Pre-align Images" msgstr "Pre-align Images" #: f.combine.cc:4532 f.combine.cc:5638 msgid "lens mm" msgstr "lens mm" #: f.combine.cc:4537 f.combine.cc:5643 msgid "no auto warp" msgstr "no auto warp" #: f.combine.cc:4539 f.combine.cc:5645 f.widgets.cc:445 f.widgets.cc:683 msgid "Resize" msgstr "Resize" #: f.combine.cc:4540 f.combine.cc:5646 msgid "resize window" msgstr "resize window" #: f.combine.cc:4548 f.combine.cc:5654 msgid "do not warp images during auto-alignment" msgstr "do not warp images during auto-alignment" #: f.combine.cc:4593 f.combine.cc:5699 msgid "use two images only" msgstr "use two images only" #: f.combine.cc:4621 f.combine.cc:4825 f.combine.cc:5011 f.combine.cc:5725 #: f.combine.cc:5929 f.combine.cc:6115 msgid "Too little overlap, cannot align" msgstr "Too little overlap, cannot align" #: f.combine.cc:5089 f.combine.cc:6193 msgid "Match Brightness and Color" msgstr "Match Brightness and Color" #: f.combine.cc:5134 msgid "Select image" msgstr "Select image" #: f.combine.cc:5149 f.combine.cc:6252 msgid "auto color" msgstr "auto color" #: f.combine.cc:5150 f.combine.cc:6253 msgid "file color" msgstr "file color" #: f.combine.cc:5156 f.combine.cc:6259 msgid "mouse warp" msgstr "mouse warp" #: f.combine.cc:5159 msgid "curved image" msgstr "curved image" #: f.combine.cc:5569 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:6469 msgid "pano tools (hugin) not installed" msgstr "pano tools (hugin) not installed" #: f.combine.cc:6478 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:173 f.widgets.cc:437 f.widgets.cc:682 msgid "Trim/Rotate" msgstr "Trim/Rotate" #: f.edit.cc:185 f.edit.cc:586 msgid "ratio" msgstr "ratio" #: f.edit.cc:189 msgid "trim size:" msgstr "trim size:" #: f.edit.cc:194 msgid "Lock Ratio" msgstr "Lock Ratio" #: f.edit.cc:199 msgid "Customize" msgstr "Customize" #: f.edit.cc:207 msgid "Rotate: degrees" msgstr "Rotate: degrees" #: f.edit.cc:209 msgid "auto-trim" msgstr "auto-trim" #: f.edit.cc:582 msgid "Trim Buttons" msgstr "Trim Buttons" #: f.edit.cc:585 msgid "label" msgstr "label" #: f.edit.cc:1411 msgid "Upright Image" msgstr "Upright Image" #: f.edit.cc:1477 msgid "rotation unknown" msgstr "rotation unknown" #: f.edit.cc:2029 f.widgets.cc:441 f.widgets.cc:686 msgid "Retouch Combo" msgstr "Retouch Combo" #: f.edit.cc:2050 msgid "Amplifier" msgstr "Amplifier" #: f.edit.cc:2051 fotoxx.h:1091 msgid "Brightness" msgstr "Brightness" #: f.edit.cc:2052 f.effects.cc:3364 fotoxx.h:1099 msgid "Contrast" msgstr "Contrast" #: f.edit.cc:2053 msgid "Low Color" msgstr "Low Color" #: f.edit.cc:2054 msgid "Warmer" msgstr "Warmer" #: f.edit.cc:2055 f.effects.cc:1011 msgid "Dark Areas" msgstr "Dark Areas" #: f.edit.cc:2064 msgid "Max." msgstr "Max." #: f.edit.cc:2065 f.edit.cc:2066 f.edit.cc:2067 msgid "High" msgstr "High" #: f.edit.cc:2068 msgid "Cooler" msgstr "Cooler" #: f.edit.cc:2069 msgid "Bright" msgstr "Bright" #: f.edit.cc:2072 f.tools.cc:1631 msgid "Brightness Distribution" msgstr "Brightness Distribution" #: f.edit.cc:2075 msgid "Click for white balance or black level" msgstr "Click for white balance or black level" #: f.edit.cc:2078 msgid "Settings File" msgstr "Settings File" #: f.edit.cc:2082 msgid "recall previous settings used" msgstr "recall previous settings used" #: f.edit.cc:2726 msgid "Adjust Brightness Distribution" msgstr "Adjust Brightness Distribution" #: f.edit.cc:2765 msgid "Low Cutoff" msgstr "Low Cutoff" #: f.edit.cc:2766 msgid "High Cutoff" msgstr "High Cutoff" #: f.edit.cc:2767 msgid "Low Flatten" msgstr "Low Flatten" #: f.edit.cc:2768 msgid "Mid Flatten" msgstr "Mid Flatten" #: f.edit.cc:2769 msgid "High Flatten" msgstr "High Flatten" #: f.edit.cc:2770 msgid "Low Stretch" msgstr "Low Stretch" #: f.edit.cc:2771 msgid "Mid Stretch" msgstr "Mid Stretch" #: f.edit.cc:2772 msgid "High Stretch" msgstr "High Stretch" #: f.edit.cc:3102 msgid "Zonal Flatten Brightness" msgstr "Zonal Flatten Brightness" #: f.edit.cc:3135 msgid "Zones" msgstr "Zones" #: f.edit.cc:3142 msgid "Deband Dark" msgstr "Deband Dark" #: f.edit.cc:3145 msgid "Deband Bright" msgstr "Deband Bright" #: f.edit.cc:3594 f.widgets.cc:444 f.widgets.cc:689 msgid "Tone Mapping" msgstr "Tone Mapping" #: f.edit.cc:3625 msgid "low" msgstr "low" #: f.edit.cc:3627 msgid "high" msgstr "high" #: f.edit.cc:3630 msgid "Amplify" msgstr "Amplify" #: f.edit.cc:4034 msgid "Resize Image" msgstr "Resize Image" #: f.edit.cc:4059 msgid "W/H Ratio:" msgstr "W/H Ratio:" #: f.edit.cc:4061 msgid "Lock" msgstr "Lock" #: f.edit.cc:4063 msgid "use previous settings" msgstr "use previous settings" #: f.edit.cc:4262 f.widgets.cc:446 msgid "Flip" msgstr "Flip" #: f.edit.cc:4368 msgid "+Version" msgstr "+Version" #: f.edit.cc:4391 msgid "Write Text on Image" msgstr "Write Text on Image" #: f.edit.cc:4392 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:4441 f.mashup.cc:2437 msgid "Text" msgstr "Text" #: f.edit.cc:4446 msgid "Use metadata key" msgstr "Use metadata key" #: f.edit.cc:4451 msgid "Use text file" msgstr "Use text file" #: f.edit.cc:4470 f.mashup.cc:2455 msgid "text" msgstr "text" #: f.edit.cc:4471 f.edit.cc:5309 f.mashup.cc:2456 f.mashup.cc:2798 msgid "backing" msgstr "backing" #: f.edit.cc:4472 f.edit.cc:5310 f.mashup.cc:2457 f.mashup.cc:2799 msgid "outline" msgstr "outline" #: f.edit.cc:4473 f.edit.cc:5311 f.mashup.cc:2458 f.mashup.cc:2800 msgid "shadow" msgstr "shadow" #: f.edit.cc:4499 msgid "save to current file" msgstr "save to current file" #: f.edit.cc:4500 f.file.cc:1909 msgid "save as new file version" msgstr "save as new file version" #: f.edit.cc:4501 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:4662 f.mashup.cc:2557 f.tools.cc:1068 msgid "select font" msgstr "select font" #: f.edit.cc:4926 msgid "text file is defective" msgstr "text file is defective" #: f.edit.cc:5249 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:5280 msgid "Write Line or Arrow on Image" msgstr "Write Line or Arrow on Image" #: f.edit.cc:5288 f.mashup.cc:2777 msgid "Line length" msgstr "Line length" #: f.edit.cc:5295 f.mashup.cc:2784 msgid "Arrow head" msgstr "Arrow head" #: f.edit.cc:5308 f.mashup.cc:2797 msgid "line" msgstr "line" #: f.edit.cc:5337 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:5926 f.widgets.cc:449 msgid "Paint Edits" msgstr "Paint Edits" #: f.edit.cc:5933 f.edit.cc:6162 fotoxx-15.11.cc:3179 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "Select area cannot be kept.\n" "Continue?" #: f.edit.cc:5941 f.edit.cc:6170 f.tools.cc:2157 msgid "Edit function must be active" msgstr "Edit function must be active" #: f.edit.cc:5946 msgid "Cannot use Paint Edits" msgstr "Cannot use Paint Edits" #: f.edit.cc:5975 msgid "power: center" msgstr "power: center" #: f.edit.cc:5980 msgid "reset area" msgstr "reset area" #: f.edit.cc:6155 f.widgets.cc:450 msgid "Leverage Edits" msgstr "Leverage Edits" #: f.edit.cc:6156 msgid "Edit Function Amplifier" msgstr "Edit Function Amplifier" #: f.edit.cc:6175 msgid "Cannot use Leverage Edits" msgstr "Cannot use Leverage Edits" #: f.edit.cc:6208 msgid "minimum" msgstr "minimum" #: f.edit.cc:6210 msgid "maximum" msgstr "maximum" #: f.edit.cc:6510 msgid "Edit Plugins" msgstr "Edit Plugins" #: f.edit.cc:6511 msgid "Edit plugins menu" msgstr "Edit plugins menu" #: f.edit.cc:6519 msgid "Run as Fotoxx edit function" msgstr "Run as Fotoxx edit function" #: f.edit.cc:6757 msgid "Plugin working ..." msgstr "Plugin working ..." #: f.edit.cc:6766 msgid "plugin failed" msgstr "plugin failed" #: f.effects.cc:64 msgid "Set color depth to 1-16 bits" msgstr "Set color depth to 1-16 bits" #: f.effects.cc:76 msgid "Set Color Depth" msgstr "Set Color Depth" #: f.effects.cc:220 msgid "Convert to Sketch" msgstr "Convert to Sketch" #: f.effects.cc:296 msgid "Clip Level" msgstr "Clip Level" #: f.effects.cc:300 msgid "Algorithm" msgstr "Algorithm" #: f.effects.cc:305 msgid "Foreground" msgstr "Foreground" #: f.effects.cc:308 msgid "Background" msgstr "Background" #: f.effects.cc:714 f.widgets.cc:486 msgid "Line Drawing" msgstr "Line Drawing" #: f.effects.cc:727 msgid "black/white" msgstr "black/white" #: f.effects.cc:1005 f.widgets.cc:487 msgid "Color Drawing" msgstr "Color Drawing" #: f.effects.cc:1012 msgid "Bright Areas" msgstr "Bright Areas" #: f.effects.cc:1266 f.widgets.cc:488 msgid "Graduated Blur" msgstr "Graduated Blur" #: f.effects.cc:1270 msgid "Contrast Limit" msgstr "Contrast Limit" #: f.effects.cc:1273 f.repair.cc:775 msgid "Blur Radius" msgstr "Blur Radius" #: f.effects.cc:1487 msgid "Simulate Embossing" msgstr "Simulate Embossing" #: f.effects.cc:1504 msgid "depth" msgstr "depth" #: f.effects.cc:1706 msgid "Simulate Tiles" msgstr "Simulate Tiles" #: f.effects.cc:1710 msgid "tile size" msgstr "tile size" #: f.effects.cc:1713 msgid "tile gap" msgstr "tile gap" #: f.effects.cc:1716 msgid "3D depth" msgstr "3D depth" #: f.effects.cc:1940 msgid "Convert Image to Dots" msgstr "Convert Image to Dots" #: f.effects.cc:1944 msgid "dot size" msgstr "dot size" #: f.effects.cc:2169 msgid "Simulate Painting" msgstr "Simulate Painting" #: f.effects.cc:2173 msgid "color depth" msgstr "color depth" #: f.effects.cc:2177 msgid "patch area goal" msgstr "patch area goal" #: f.effects.cc:2181 msgid "req. color match" msgstr "req. color match" #: f.effects.cc:2185 msgid "borders" msgstr "borders" #: f.effects.cc:2758 f.widgets.cc:493 msgid "Vignette" msgstr "Vignette" #: f.effects.cc:3107 msgid "Add Texture" msgstr "Add Texture" #: f.effects.cc:3324 msgid "Background Pattern" msgstr "Background Pattern" #: f.effects.cc:3328 msgid "Pattern File:" msgstr "Pattern File:" #: f.effects.cc:3333 msgid "Zoom" msgstr "Zoom" #: f.effects.cc:3336 msgid "Geometry" msgstr "Geometry" #: f.effects.cc:3337 msgid "Calculate" msgstr "Calculate" #: f.effects.cc:3347 f.widgets.cc:495 msgid "Pattern" msgstr "Pattern" #: f.effects.cc:3354 msgid "Overlap" msgstr "Overlap" #: f.effects.cc:3361 msgid "Opacity" msgstr "Opacity" #: f.effects.cc:3429 msgid "choose pattern tile" msgstr "choose pattern tile" #: f.effects.cc:3816 msgid "Create Mosaic" msgstr "Create Mosaic" #: f.effects.cc:3902 msgid "Tile" msgstr "Tile" #: f.effects.cc:3910 f.widgets.cc:490 msgid "Tiles" msgstr "Tiles" #: f.effects.cc:3916 msgid "Tile blending" msgstr "Tile blending" #: f.effects.cc:3996 #, c-format msgid "exceeded max. tiles: %d" msgstr "exceeded max. tiles: %d" #: f.effects.cc:4011 #, c-format msgid "only %d tile images found" msgstr "only %d tile images found" #: f.effects.cc:4404 f.widgets.cc:497 msgid "Custom Kernel" msgstr "Custom Kernel" #: f.effects.cc:4408 msgid "Kernel size" msgstr "Kernel size" #: f.effects.cc:4411 msgid "Divisor" msgstr "Divisor" #: f.effects.cc:4414 msgid "Data file" msgstr "Data file" #: f.effects.cc:4450 fotoxx-15.11.cc:3440 msgid "Load settings from file" msgstr "Load settings from file" #: f.effects.cc:4711 f.widgets.cc:498 msgid "Make Waves" msgstr "Make Waves" #: f.effects.cc:4718 msgid "wavelength" msgstr "wavelength" #: f.effects.cc:4719 msgid "amplitude" msgstr "amplitude" #: f.effects.cc:4720 msgid "variance" msgstr "variance" #: f.effects.cc:4731 msgid "perspective" msgstr "perspective" #: f.effects.cc:4905 msgid "Pull image using the mouse." msgstr "Pull image using the mouse." #: f.effects.cc:4926 f.widgets.cc:499 msgid "Directed Blur" msgstr "Directed Blur" #: f.effects.cc:4930 msgid "blur span" msgstr "blur span" #: f.effects.cc:4933 msgid "intensity" msgstr "intensity" #: f.effects.cc:5099 f.widgets.cc:500 msgid "Spherical Projection" msgstr "Spherical Projection" #: f.effects.cc:5143 msgid "Magnify" msgstr "Magnify" #: f.file.cc:186 msgid "" "use EXIF photo date or \n" " file modification date" msgstr "" "use EXIF photo date or \n" " file modification date" #: f.file.cc:203 f.widgets.cc:572 msgid "File" msgstr "File" #: f.file.cc:357 msgid "UFraw not installed" msgstr "UFraw not installed" #: f.file.cc:373 f.widgets.cc:691 msgid "Open RAW file (ufraw)" msgstr "Open RAW file (ufraw)" #: f.file.cc:378 f.file.cc:449 msgid "RAW type not registered in User Settings" msgstr "RAW type not registered in User Settings" #: f.file.cc:444 f.widgets.cc:692 msgid "Open RAW file (Raw Therapee)" msgstr "Open RAW file (Raw Therapee)" #: f.file.cc:541 f.widgets.cc:395 msgid "Open Image File" msgstr "Open Image File" #: f.file.cc:560 msgid "unknown file type" msgstr "unknown file type" #: f.file.cc:753 msgid "Create Blank Image" msgstr "Create Blank Image" #: f.file.cc:755 msgid "file name" msgstr "file name" #: f.file.cc:788 msgid "supply a file name" msgstr "supply a file name" #: f.file.cc:934 f.widgets.cc:400 msgid "Rename Image File" msgstr "Rename Image File" #: f.file.cc:941 msgid "Old Name" msgstr "Old Name" #: f.file.cc:951 msgid "previous name" msgstr "previous name" #: f.file.cc:952 msgid "add 1" msgstr "add 1" #: f.file.cc:1120 msgid "Copy Image File" msgstr "Copy Image File" #: f.file.cc:1121 msgid "Move Image File" msgstr "Move Image File" #: f.file.cc:1164 msgid "new location" msgstr "new location" #: f.file.cc:1236 msgid "new location is not a directory" msgstr "new location is not a directory" #: f.file.cc:1276 f.file.cc:1518 f.file.cc:1656 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "delete failed: \n" " %s" #: f.file.cc:1363 f.widgets.cc:401 msgid "Trash Image File" msgstr "Trash Image File" #: f.file.cc:1364 f.file.cc:1571 msgid "(automatic step to next image)" msgstr "(automatic step to next image)" #: f.file.cc:1432 msgid "" "Linux standard trash does not work. \n" "Desktop trash folder will be created." msgstr "" "Linux standard trash does not work. \n" "Desktop trash folder will be created." #: f.file.cc:1435 msgid "" "Linux and Desktop trash do not work. \n" "Permanently delete the image file?" msgstr "" "Linux and Desktop trash do not work. \n" "Permanently delete the image file?" #: f.file.cc:1438 #, c-format msgid "Cannot create trash folder: %s" msgstr "Cannot create trash folder: %s" #: f.file.cc:1450 msgid "Move read-only file to trash?" msgstr "Move read-only file to trash?" #: f.file.cc:1505 #, c-format msgid "error: %s" msgstr "error: %s" #: f.file.cc:1534 f.file.cc:1673 f.file.cc:2508 msgid "no more images" msgstr "no more images" #: f.file.cc:1570 msgid "Delete Image File - CANNOT BE REVERSED" msgstr "Delete Image File - CANNOT BE REVERSED" #: f.file.cc:1646 msgid "Delete read-only file?" msgstr "Delete read-only file?" #: f.file.cc:1891 msgid "Save Image File" msgstr "Save Image File" #: f.file.cc:1901 msgid "new version" msgstr "new version" #: f.file.cc:1902 msgid "new file" msgstr "new file" #: f.file.cc:1903 msgid "replace file" msgstr "replace file" #: f.file.cc:1910 f.file.cc:2281 msgid "save as new file name or type" msgstr "save as new file name or type" #: f.file.cc:1912 msgid "replace old file (OVERWRITE)" msgstr "replace old file (OVERWRITE)" #: f.file.cc:1950 f.file.cc:2057 msgid "cannot save as RAW type" msgstr "cannot save as RAW type" #: f.file.cc:2051 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:2191 msgid "Unable to copy EXIF/IPTC data" msgstr "Unable to copy EXIF/IPTC data" #: f.file.cc:2299 f.file.cc:2302 msgid "make current" msgstr "make current" #: f.file.cc:2300 msgid "(new file becomes current file)" msgstr "(new file becomes current file)" #: f.file.cc:2409 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Overwrite file? \n" " %s" #: f.file.cc:2555 f.widgets.cc:554 msgid "Quick Start" msgstr "Quick Start" #: f.file.cc:2558 f.widgets.cc:555 msgid "User Guide" msgstr "User Guide" #: f.file.cc:2561 f.widgets.cc:556 msgid "User Guide Changes" msgstr "User Guide Changes" #: f.file.cc:2564 f.widgets.cc:558 msgid "README" msgstr "README" #: f.file.cc:2567 f.widgets.cc:557 msgid "Edit Functions Summary" msgstr "Edit Functions Summary" #: f.file.cc:2570 f.widgets.cc:559 msgid "Change Log" msgstr "Change Log" #: f.file.cc:2573 f.widgets.cc:560 msgid "Log File" msgstr "Log File" #: f.file.cc:2576 f.widgets.cc:561 msgid "Translations" msgstr "Translations" #: f.file.cc:2579 f.widgets.cc:562 msgid "Home Page" msgstr "Home Page" #: f.file.cc:2582 f.widgets.cc:563 msgid "About" msgstr "About" #: f.file.cc:2586 f.widgets.cc:584 f.widgets.cc:606 fotoxx.h:1123 msgid "Help" msgstr "Help" #: f.file.cc:2739 f.file.cc:2808 #, c-format msgid "file not found: %s" msgstr "file not found: %s" #: f.file.cc:2745 f.file.cc:2814 #, c-format msgid "file type not supported: %s" msgstr "file type not supported: %s" #: f.gallery.cc:918 f.gallery.cc:1007 f.widgets.cc:604 msgid "Scroll" msgstr "Scroll" #: f.gallery.cc:922 msgid "Sync" msgstr "Sync" #: f.gallery.cc:932 f.repair.cc:5845 f.repair.cc:5850 fotoxx.h:1144 msgid "Open" msgstr "Open" #: f.gallery.cc:933 msgid "change directory" msgstr "change directory" #: f.gallery.cc:941 msgid "GoTo" msgstr "GoTo" #: f.gallery.cc:947 f.widgets.cc:599 msgid "Sort" msgstr "Sort" #: f.gallery.cc:953 f.gallery.cc:1517 f.gallery.cc:1518 f.gallery.cc:1520 #: f.widgets.cc:597 msgid "Zoom+" msgstr "Zoom+" #: f.gallery.cc:963 f.gallery.cc:1519 f.gallery.cc:1521 f.widgets.cc:598 msgid "Zoom-" msgstr "Zoom-" #: f.gallery.cc:975 f.gallery.cc:1525 msgid "Row↑" msgstr "Row↑" #: f.gallery.cc:983 f.gallery.cc:1526 msgid "Row↓" msgstr "Row↓" #: f.gallery.cc:991 f.gallery.cc:1523 f.gallery.cc:1530 f.widgets.cc:602 msgid "Page↑" msgstr "Page↑" #: f.gallery.cc:999 f.gallery.cc:1524 f.gallery.cc:1531 f.widgets.cc:603 msgid "Page↓" msgstr "Page↓" #: f.gallery.cc:1013 f.gallery.cc:1528 f.widgets.cc:600 msgid "First" msgstr "First" #: f.gallery.cc:1014 f.gallery.cc:1529 f.widgets.cc:601 msgid "Last" msgstr "Last" #: f.gallery.cc:1127 msgid "Choose image directory" msgstr "Choose image directory" #: f.gallery.cc:1134 f.gallery.cc:1148 msgid "recent" msgstr "recent" #: f.gallery.cc:1135 f.gallery.cc:1153 msgid "newest" msgstr "newest" #: f.gallery.cc:1217 msgid "no albums found" msgstr "no albums found" #: f.gallery.cc:1224 msgid "Choose album" msgstr "Choose album" #: f.gallery.cc:1274 msgid "Gallery Sort" msgstr "Gallery Sort" #: f.gallery.cc:1278 msgid "File Name" msgstr "File Name" #: f.gallery.cc:1279 msgid "File Mod Date/Time" msgstr "File Mod Date/Time" #: f.gallery.cc:1280 msgid "Photo Date/Time (EXIF)" msgstr "Photo Date/Time (EXIF" #: f.gallery.cc:1282 msgid "ascending" msgstr "ascending" #: f.gallery.cc:1283 msgid "descending" msgstr "descending" #: f.gallery.cc:2212 fotoxx.h:1169 msgid "Select Files" msgstr "Select Files" #: f.gallery.cc:2672 msgid "Click list position. Click thumbnail to add." msgstr "Click list position. Click thumbnail to add." #: f.gallery.cc:2698 f.gallery.cc:2946 msgid "Edit Bookmarks" msgstr "Edit Bookmarks" #: f.gallery.cc:2873 msgid "unable to save bookmarks file" msgstr "unable to save bookmarks file" #: f.gallery.cc:2946 msgid "Go To Bookmark" msgstr "Go To Bookmark" #: f.gallery.cc:3003 f.meta.cc:2844 f.repair.cc:7473 msgid "file not found" msgstr "file not found" #: 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:241 msgid "choose layout file" msgstr "choose layout 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:417 msgid "Edit Images" msgstr "Edit Images" #: f.mashup.cc:418 f.mashup.cc:2432 msgid "Edit Text" msgstr "Edit Text" #: f.mashup.cc:419 msgid "Edit Line" msgstr "Edit Line" #: f.mashup.cc:420 msgid "Rescale" msgstr "Rescale" #: f.mashup.cc:423 msgid "add or edit images" msgstr "add or edit images" #: f.mashup.cc:424 msgid "add or edit text" msgstr "add or edit text" #: f.mashup.cc:425 msgid "add or edit lines/arrows" msgstr "add or edit lines/arrows" #: f.mashup.cc:426 msgid "change project scale" msgstr "change project scale" #: f.mashup.cc:427 msgid "project complete" msgstr "project complete" #: f.mashup.cc:428 msgid "cancel project" msgstr "cancel project" #: f.mashup.cc:446 msgid "rescale project" msgstr "rescale project" #: f.mashup.cc:501 msgid "save Mashup output file" msgstr "save Mashup output file" #: f.mashup.cc:512 msgid "save Mashup project file?" msgstr "save Mashup project file?" #: f.mashup.cc:524 msgid "delete Mashup project file?" msgstr "delete Mashup project file?" #: f.mashup.cc:583 msgid "Open Project" msgstr "Open Project" #: f.mashup.cc:612 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "layout image file missing: \n" " %s" #: f.mashup.cc:669 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "overlay image file missing: \n" " %s" #: f.mashup.cc:974 msgid "project file is defective" msgstr "project file is defective" #: f.mashup.cc:1004 msgid "Save Project" msgstr "Save Project" #: f.mashup.cc:1145 msgid "layout exceeds 2 gigabytes" msgstr "layout exceeds 2 gigabytes" #: f.mashup.cc:1219 msgid "Click image to select, drag image to move." msgstr "Click image to select, drag image to move." #: f.mashup.cc:1220 msgid "Make black margins transparent" msgstr "Make black margins transparent" #: f.mashup.cc:1259 msgid "Current image:" msgstr "Current image:" #: f.mashup.cc:1263 msgid "Cycle through images:" msgstr "Cycle through images:" #: f.mashup.cc:1270 msgid "Scale" msgstr "Scale" #: f.mashup.cc:1279 msgid "Stacking Order" msgstr "Stacking Order" #: f.mashup.cc:1280 msgid "raise" msgstr "raise" #: f.mashup.cc:1281 msgid "lower" msgstr "lower" #: f.mashup.cc:1284 msgid "Base Transparency" msgstr "Base Transparency" #: f.mashup.cc:1288 msgid "Var. Transparency" msgstr "Var. Transparency" #: f.mashup.cc:1289 msgid "Paint" msgstr "Paint" #: f.mashup.cc:1292 msgid "Bend and fine-align" msgstr "Bend and fine-align" #: f.mashup.cc:1293 msgid "Warp" msgstr "Warp" #: f.mashup.cc:1302 msgid "Margins" msgstr "Margins" #: f.mashup.cc:1303 msgid "Hard" msgstr "Hard" #: f.mashup.cc:1304 f.repair.cc:5441 msgid "Blend" msgstr "Blend" #: f.mashup.cc:1318 msgid "add images to layout" msgstr "add images to layout" #: f.mashup.cc:1557 msgid "Paint Image Transparencies" msgstr "Paint Image Transparencies" #: f.mashup.cc:1575 msgid "Gradual" msgstr "Gradual" #: f.mashup.cc:1580 fotoxx.h:1148 msgid "Power" msgstr "Power" #: f.mashup.cc:1814 msgid "Pull on the image with the mouse." msgstr "Pull on the image with the mouse." #: f.mashup.cc:1830 msgid "Warp Image" msgstr "Warp Image" #: f.mashup.cc:2263 #, c-format msgid "exceeded %d images" msgstr "exceeded %d images" #: f.mashup.cc:2405 msgid "Enter text, [Add] to layout, edit properties." msgstr "Enter text, [Add] to layout, edit properties." #: f.mashup.cc:2485 msgid "Text File:" msgstr "Text File:" #: f.mashup.cc:2489 msgid "add entered text to layout" msgstr "add entered text to layout" #: f.mashup.cc:2618 msgid "click position to add text" msgstr "click position to add text" #: f.mashup.cc:2624 #, c-format msgid "exceeded %d text entries" msgstr "exceeded %d text entries" #: f.mashup.cc:2747 msgid "Set line properties, [Add] to layout, edit." msgstr "Set line properties, [Add] to layout, edit." #: f.mashup.cc:2771 msgid "Edit Line/Arrow" msgstr "Edit Line/Arrow" #: f.mashup.cc:2826 msgid "add line/arrow to layout" msgstr "add line/arrow to layout" #: f.mashup.cc:2919 msgid "click position to add line" msgstr "click position to add line" #: f.mashup.cc:2925 #, c-format msgid "exceeded %d line entries" msgstr "exceeded %d line entries" #: f.meta.cc:180 f.widgets.cc:669 msgid "View Metadata" msgstr "View Metadata" #: f.meta.cc:430 f.meta.cc:1807 f.widgets.cc:412 f.widgets.cc:670 msgid "Edit Metadata" msgstr "Edit Metadata" #: f.meta.cc:433 f.meta.cc:3819 msgid "save metadata to file" msgstr "save metadata to file" #: f.meta.cc:442 msgid "Image Date" msgstr "Image Date" #: f.meta.cc:445 msgid "Time" msgstr "Time" #: f.meta.cc:451 msgid "Rating (stars):" msgstr "Rating (stars):" #: f.meta.cc:463 msgid "Caption" msgstr "Caption" #: f.meta.cc:469 msgid "Comments" msgstr "Comments" #: f.meta.cc:477 msgid "Enter New Tag" msgstr "Enter New Tag" #: f.meta.cc:484 f.meta.cc:5003 msgid "Matching Tags" msgstr "Matching Tags" #: f.meta.cc:492 msgid "Image Tags" msgstr "Image Tags" #: f.meta.cc:498 msgid "Recent Tags" msgstr "Recent Tags" #: f.meta.cc:507 f.meta.cc:2029 f.meta.cc:5010 msgid "Defined Tags Category" msgstr "Defined Tags Category" #: f.meta.cc:898 msgid "date format is YYYY-MM-DD" msgstr "date format is YYYY-MM-DD" #: f.meta.cc:902 msgid "date is invalid" msgstr "date is invalid" #: f.meta.cc:936 msgid "time format is HH:MM [:SS]" msgstr "time format is HH:MM [:SS]" #: f.meta.cc:940 msgid "time is invalid" msgstr "time is invalid" #: f.meta.cc:1025 fotoxx.h:1133 msgid "Manage Tags" msgstr "Manage Tags" #: f.meta.cc:1025 msgid "orphan tags" msgstr "orphan tags" #: f.meta.cc:1029 msgid "category" msgstr "category" #: f.meta.cc:1032 msgid "tag" msgstr "tag" #: f.meta.cc:1039 msgid "Defined Tags:" msgstr "Defined Tags:" #: f.meta.cc:1811 f.meta.cc:2474 msgid "key name" msgstr "key name" #: f.meta.cc:1813 f.meta.cc:2475 msgid "key value" msgstr "key value" #: f.meta.cc:1900 f.widgets.cc:414 msgid "Delete Metadata" msgstr "Delete Metadata" #: f.meta.cc:1902 fotoxx.h:1082 msgid "All" msgstr "All" #: f.meta.cc:1903 msgid "One Key:" msgstr "One Key:" #: f.meta.cc:2006 f.widgets.cc:415 f.widgets.cc:540 msgid "Batch Add/Remove Tags" msgstr "Batch Add/Remove Tags" #: f.meta.cc:2017 msgid "tags to add" msgstr "tags to add" #: f.meta.cc:2018 msgid "tags to remove" msgstr "tags to remove" #: f.meta.cc:2102 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " too many tags" #: f.meta.cc:2217 msgid "specify files and tags" msgstr "specify files and tags" #: f.meta.cc:2277 msgid "tag names file" msgstr "tag names file" #: f.meta.cc:2349 #, 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:2461 msgid "Batch Metadata" msgstr "Batch Metadata" #: f.meta.cc:2468 msgid "Short List" msgstr "Short List" #: f.meta.cc:2469 msgid "Full List" msgstr "Full List" #: f.meta.cc:2511 msgid "enter key names" msgstr "enter key names" #: f.meta.cc:2517 fotoxx.h:1141 msgid "no files selected" msgstr "no files selected" #: f.meta.cc:2643 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:3276 f.meta.cc:4011 f.meta.cc:4277 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "bad latitude/longitude: %s %s" #: f.meta.cc:3320 f.meta.cc:3682 f.meta.cc:4486 f.meta.cc:4894 msgid "-noindex in use, disabled" msgstr "-noindex in use, disabled" #: f.meta.cc:3336 msgid "choose map file" msgstr "choose map file" #: f.meta.cc:3444 msgid "" "fotoxx-maps package not installed \n" "(see http://kornelix.com/packages and /tarballs)" msgstr "" "fotoxx-maps package not installed \n" "(see http://kornelix.com/packages and /tarballs)" #: f.meta.cc:3449 #, c-format msgid "map file %s is missing" msgstr "map file %s is missing" #: f.meta.cc:3453 #, 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:3731 msgid "No matching images found" msgstr "No matching images found" #: f.meta.cc:3771 msgid "search range (km)" msgstr "search range (km)" #: f.meta.cc:3804 f.widgets.cc:418 f.widgets.cc:671 msgid "Edit Geotags" msgstr "Edit Geotags" #: f.meta.cc:3805 msgid "Geocoding web service courtesy of" msgstr "Geocoding web service courtesy of" #: f.meta.cc:3821 f.meta.cc:4060 f.meta.cc:5783 msgid "city" msgstr "city" #: f.meta.cc:3824 f.meta.cc:4063 f.meta.cc:5786 msgid "country" msgstr "country" #: f.meta.cc:3906 f.meta.cc:4194 msgid "city not found" msgstr "city not found" #: f.meta.cc:4027 f.widgets.cc:419 f.widgets.cc:543 msgid "Batch Add Geotags" msgstr "Batch Add Geotags" #: f.meta.cc:4256 msgid "" "data is incomplete \n" " proceed?" msgstr "" "data is incomplete \n" " proceed?" #: f.meta.cc:4313 msgid "choose city" msgstr "choose city" #: f.meta.cc:4400 msgid "not found" msgstr "not found" #: f.meta.cc:4401 msgid "city and country required" msgstr "city and country required" #: f.meta.cc:4501 msgid "Report Geotag Groups" msgstr "Report Geotag Groups" #: f.meta.cc:4502 msgid "Group by country" msgstr "Group by country" #: f.meta.cc:4503 msgid "Group by country/city" msgstr "Group by country/city" #: f.meta.cc:4504 msgid "Group by country/city/date" msgstr "Group by country/city/date" #: f.meta.cc:4507 msgid "Combine within" msgstr "Combine within" #: f.meta.cc:4509 msgid "days" msgstr "days" #: f.meta.cc:4625 msgid "geotag groups" msgstr "geotag groups" #: f.meta.cc:4931 msgid "Search Image Metadata" msgstr "Search Image Metadata" #: f.meta.cc:4935 msgid "images to search:" msgstr "images to search:" #: f.meta.cc:4936 msgid "all" msgstr "all" #: f.meta.cc:4937 msgid "current set only" msgstr "current set only" #: f.meta.cc:4940 msgid "matching images:" msgstr "matching images:" #: f.meta.cc:4941 msgid "new set" msgstr "new set" #: f.meta.cc:4942 msgid "add to set" msgstr "add to set" #: f.meta.cc:4943 msgid "remove" msgstr "remove" #: f.meta.cc:4946 msgid "report type:" msgstr "report type:" #: f.meta.cc:4947 msgid "gallery" msgstr "gallery" #: f.meta.cc:4948 fotoxx-15.11.cc:791 msgid "metadata" msgstr "metadata" #: f.meta.cc:4954 msgid "date range" msgstr "date range" #: f.meta.cc:4955 msgid "stars range" msgstr "stars range" #: f.meta.cc:4956 msgid "search tags" msgstr "search tags" #: f.meta.cc:4957 msgid "search text" msgstr "search text" #: f.meta.cc:4958 msgid "search files" msgstr "search files" #: f.meta.cc:4963 msgid "(yyyymmdd)" msgstr "(yyyymmdd)" #: f.meta.cc:4968 msgid "last version only" msgstr "last version only" #: f.meta.cc:4970 msgid "all/any" msgstr "all/any" #: f.meta.cc:4989 msgid "other criteria" msgstr "other criteria" #: f.meta.cc:4993 msgid "other" msgstr "other" #: f.meta.cc:4999 msgid "Enter Search Tag" msgstr "Enter Search Tag" #: f.meta.cc:5294 #, c-format msgid "not a defined tag: %s" msgstr "not a defined tag: %s" #: f.meta.cc:5325 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:5332 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:5376 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "search dates not reasonable \n" " %s %s" #: f.meta.cc:5394 msgid "stars range not reasonable" msgstr "stars range not reasonable" #: f.meta.cc:5639 #, c-format msgid "images added: %d removed: %d new count: %d" msgstr "images added: %d removed: %d new count: %d" #: f.meta.cc:5642 msgid "no changes made" msgstr "no changes made" #: f.meta.cc:5780 msgid "Add Geotags Search Criteria" msgstr "Add Geotags Search Criteria" #: f.meta.cc:5799 msgid "range (km)" msgstr "range (km)" #: f.meta.cc:5892 msgid "error in latitude/longitude/range" msgstr "error in latitude/longitude/range" #: f.meta.cc:5956 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:5980 msgid "Additional Items for Report" msgstr "Additional Items for Report" #: f.meta.cc:5986 msgid "Keyword" msgstr "Keyword" #: f.meta.cc:5993 msgid "Match Criteria" msgstr "Match Criteria" #: f.meta.cc:6553 msgid "image index is missing" msgstr "image index is missing" #: f.repair.cc:1041 msgid "Apply repeatedly while watching the image." msgstr "Apply repeatedly while watching the image." #: f.repair.cc:1042 msgid "Measure" msgstr "Measure" #: f.repair.cc:1078 msgid "Noise Reduction" msgstr "Noise Reduction" #: f.repair.cc:1099 msgid "Flatten 1" msgstr "Flatten 1" #: f.repair.cc:1100 msgid "Flatten 2" msgstr "Flatten 2" #: f.repair.cc:1101 msgid "Median" msgstr "Median" #: f.repair.cc:1121 msgid "dark areas" msgstr "dark areas" #: f.repair.cc:1123 msgid "all areas" msgstr "all areas" #: f.repair.cc:1246 msgid "Measure Noise" msgstr "Measure Noise" #: f.repair.cc:1247 msgid "Click on a monotone image area." msgstr "Click on a monotone image area." #: f.repair.cc:2177 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:2199 f.widgets.cc:457 msgid "Smart Erase" msgstr "Smart Erase" #: f.repair.cc:2204 fotoxx.h:1152 msgid "Radius" msgstr "Radius" #: f.repair.cc:2206 f.widgets.cc:455 msgid "Blur" msgstr "Blur" #: f.repair.cc:2209 msgid "New Area" msgstr "New Area" #: f.repair.cc:2558 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:2574 msgid "Red Eye Reduction" msgstr "Red Eye Reduction" #: f.repair.cc:3038 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 "" "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" #: f.repair.cc:3070 f.widgets.cc:459 msgid "Paint/Clone" msgstr "Paint/Clone" #: f.repair.cc:3074 msgid "paint color" msgstr "paint color" #: f.repair.cc:3077 msgid "copy from image" msgstr "copy from image" #: f.repair.cc:3084 f.repair.cc:3641 msgid "paintbrush radius" msgstr "paintbrush radius" #: f.repair.cc:3085 msgid "transparency center" msgstr "transparency center" #: f.repair.cc:3086 msgid "transparency edge" msgstr "transparency edge" #: f.repair.cc:3093 f.repair.cc:3648 msgid "gradual paint" msgstr "gradual paint" #: f.repair.cc:3100 #, c-format msgid "Undo Memory %d%c" msgstr "Undo Memory %d%c" #: f.repair.cc:3436 msgid "" "Undo memory limit has been reached. \n" "Save work with [done], then resume painting." msgstr "" "Undo memory limit has been reached. \n" "Save work with [done], then resume painting." #: f.repair.cc:3601 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "left drag: add transparency \n" "right drag: add opacity" #: f.repair.cc:3633 f.widgets.cc:460 msgid "Paint Transparency" msgstr "Paint Transparency" #: f.repair.cc:3642 msgid "strength center" msgstr "strength center" #: f.repair.cc:3643 msgid "strength edge" msgstr "strength edge" #: f.repair.cc:3858 f.widgets.cc:461 msgid "Color Mode" msgstr "Color Mode" #: f.repair.cc:3862 msgid "black/white positive" msgstr "black/white positive" #: f.repair.cc:3863 msgid "black/white negative" msgstr "black/white negative" #: f.repair.cc:3864 msgid "color positive" msgstr "color positive" #: f.repair.cc:3865 msgid "color negative" msgstr "color negative" #: f.repair.cc:3866 msgid "sepia" msgstr "sepia" #: f.repair.cc:4046 f.widgets.cc:462 msgid "Shift Colors" msgstr "Shift Colors" #: f.repair.cc:4317 msgid "+Brightness" msgstr "+Brightness" #: f.repair.cc:4318 msgid "+Red -Cyan" msgstr "+Red -Cyan" #: f.repair.cc:4319 msgid "+Green -Magenta" msgstr "+Green -Magenta" #: f.repair.cc:4320 msgid "+Blue -Yellow" msgstr "+Blue -Yellow" #: f.repair.cc:4326 fotoxx.h:1154 msgid "Red" msgstr "Red" #: f.repair.cc:4327 fotoxx.h:1120 msgid "Green" msgstr "Green" #: f.repair.cc:4328 fotoxx.h:1089 msgid "Blue" msgstr "Blue" #: f.repair.cc:4623 msgid "Color to change" msgstr "Color to change" #: f.repair.cc:4625 msgid "shift+click on image to select color" msgstr "shift+click on image to select color" #: f.repair.cc:4638 msgid "Color Hue" msgstr "Color Hue" #: f.repair.cc:4639 msgid "Saturation" msgstr "Saturation" #: f.repair.cc:4640 msgid "Lightness" msgstr "Lightness" #: f.repair.cc:4641 msgid "Color Match" msgstr "Color Match" #: f.repair.cc:4642 msgid "Adjustment" msgstr "Adjustment" #: f.repair.cc:5028 msgid "Ramp brightness across image" msgstr "Ramp brightness across image" #: f.repair.cc:5352 f.tools.cc:2048 msgid "Click image to select pixels." msgstr "Click image to select pixels." #: f.repair.cc:5383 f.widgets.cc:466 msgid "Color Ramp" msgstr "Color Ramp" #: f.repair.cc:5394 msgid "Metric:" msgstr "Metric:" #: f.repair.cc:5817 msgid "Color Match Images" msgstr "Color Match Images" #: f.repair.cc:5843 msgid "mouse radius for color sample" msgstr "mouse radius for color sample" #: f.repair.cc:5846 msgid "image for source color" msgstr "image for source color" #: f.repair.cc:5848 msgid "click on image to get source color" msgstr "click on image to get source color" #: f.repair.cc:5851 msgid "image to set matching color" msgstr "image to set matching color" #: f.repair.cc:5853 msgid "click on image to set matching color" msgstr "click on image to set matching color" #: f.repair.cc:5914 msgid "select source image color first" msgstr "select source image color first" #: f.repair.cc:6099 msgid "Change Color Profile" msgstr "Change Color Profile" #: f.repair.cc:6103 msgid "input profile" msgstr "input profile" #: f.repair.cc:6107 msgid "output profile" msgstr "output profile" #: f.repair.cc:6123 msgid "color profile" msgstr "color profile" #: f.repair.cc:6169 f.repair.cc:6175 #, c-format msgid "unknown cms profile %s" msgstr "unknown cms profile %s" #: f.repair.cc:6307 f.widgets.cc:469 msgid "Remove Dust" msgstr "Remove Dust" #: f.repair.cc:6311 msgid "spot size limit" msgstr "spot size limit" #: f.repair.cc:6314 msgid "max. brightness" msgstr "max. brightness" #: f.repair.cc:6317 msgid "min. contrast" msgstr "min. contrast" #: f.repair.cc:7098 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:7121 f.widgets.cc:471 msgid "Color Fringes" msgstr "Color Fringes" #: f.repair.cc:7320 f.widgets.cc:472 msgid "Stuck Pixels" msgstr "Stuck Pixels" #: f.repair.cc:7326 msgid "pixel group" msgstr "pixel group" #: f.repair.cc:7334 f.repair.cc:7402 msgid "stuck pixels:" msgstr "stuck pixels:" #: f.repair.cc:7430 msgid "Load Stuck Pixels" msgstr "Load Stuck Pixels" #: f.repair.cc:7432 msgid "File:" msgstr "File:" #: f.repair.cc:7457 f.repair.cc:7514 msgid "Stuck Pixels file" msgstr "Stuck Pixels file" #: f.repair.cc:7494 msgid "file format error" msgstr "file format error" #: f.tools.cc:70 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:72 msgid "Select to add, click on X to delete." msgstr "Select to add, click on X to delete." #: f.tools.cc:73 msgid "Select directory for thumbnails." msgstr "Select directory for thumbnails." #: f.tools.cc:74 msgid "" "Index function terminated. Fotoxx will close. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Index function terminated. Fotoxx will close. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." #: f.tools.cc:106 f.widgets.cc:513 msgid "Index Image Files" msgstr "Index Image Files" #: f.tools.cc:250 msgid "Choose top image directories" msgstr "Choose top image directories" #: f.tools.cc:251 msgid "Choose thumbnail directory" msgstr "Choose thumbnail directory" #: f.tools.cc:324 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:330 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" "Invalid directory: \n" " %s \n" "Please remove." #: f.tools.cc:333 #, 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:336 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" "Duplicate directory: \n" " %s \n" "Please remove." #: f.tools.cc:385 msgid "no thumbnails directory defined" msgstr "no thumbnails directory defined" #: f.tools.cc:735 msgid "COMPLETED" msgstr "COMPLETED" #: f.tools.cc:776 msgid "Indexing is required for first-time startup." msgstr "Indexing is required for first-time startup." #: f.tools.cc:838 msgid "Recent Files Gallery" msgstr "Recent Files Gallery" #: f.tools.cc:839 msgid "Newest Files Gallery" msgstr "Newest Files Gallery" #: f.tools.cc:840 msgid "Previous Gallery" msgstr "Previous Gallery" #: f.tools.cc:841 msgid "Previous Image" msgstr "Previous Image" #: f.tools.cc:842 msgid "Blank Window" msgstr "Blank Window" #: f.tools.cc:843 msgid "Directory" msgstr "Directory" #: f.tools.cc:844 msgid "Image File" msgstr "Image File" #: f.tools.cc:887 f.widgets.cc:514 msgid "User Options" msgstr "User Options" #: f.tools.cc:890 msgid "Startup Display" msgstr "Startup Display" #: f.tools.cc:901 msgid "Menu Style" msgstr "Menu Style" #: f.tools.cc:902 msgid "Icons" msgstr "Icons" #: f.tools.cc:903 msgid "Icons + Text" msgstr "Icons + Text" #: f.tools.cc:905 msgid "Icon size" msgstr "Icon size" #: f.tools.cc:909 msgid "Dialog font and size" msgstr "Dialog font and size" #: f.tools.cc:914 msgid "Image Pan/scroll:" msgstr "Image Pan/scroll:" #: f.tools.cc:917 msgid "Zooms for 2x" msgstr "Zooms for 2x" #: f.tools.cc:926 msgid "JPEG save quality" msgstr "JPEG save quality" #: f.tools.cc:933 msgid "Curve node capture distance" msgstr "Curve node capture distance" #: f.tools.cc:937 msgid "show hidden directories in gallery view" msgstr "show hidden directories in gallery view" #: f.tools.cc:940 msgid "prev/next shows last file version only" msgstr "prev/next shows last file version only" #: f.tools.cc:943 msgid "RAW command" msgstr "RAW command" #: f.tools.cc:947 msgid "RAW file types" msgstr "RAW file types" #: f.tools.cc:1029 msgid "Delete present thumbnails to make effective" msgstr "Delete present thumbnails to make effective" #: f.tools.cc:1051 msgid "Select startup directory" msgstr "Select startup directory" #: f.tools.cc:1058 msgid "Select startup image file" msgstr "Select startup image file" #: f.tools.cc:1096 msgid "startup directory is invalid" msgstr "startup directory is invalid" #: f.tools.cc:1107 msgid "startup file is invalid" msgstr "startup file is invalid" #: f.tools.cc:1247 msgid "Edit KB Shortcuts" msgstr "Edit KB Shortcuts" #: f.tools.cc:1252 msgid "shortcut key:" msgstr "shortcut key:" #: f.tools.cc:1253 msgid "(enter key)" msgstr "(enter key)" #: f.tools.cc:1379 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Reserved, cannot be used" #: f.tools.cc:1533 msgid "unable to save KB-shortcuts file" msgstr "unable to save KB-shortcuts file" #: f.tools.cc:1837 f.widgets.cc:517 msgid "Grid Lines" msgstr "Grid Lines" #: f.tools.cc:1846 msgid "x-spacing" msgstr "x-spacing" #: f.tools.cc:1847 msgid "x-count" msgstr "x-count" #: f.tools.cc:1848 msgid "x-enable" msgstr "x-enable" #: f.tools.cc:1854 msgid "y-spacing" msgstr "y-spacing" #: f.tools.cc:1855 msgid "y-count" msgstr "y-count" #: f.tools.cc:1856 msgid "y-enable" msgstr "y-enable" #: f.tools.cc:1976 f.tools.cc:1981 f.widgets.cc:518 msgid "Line Color" msgstr "Line Color" #: f.tools.cc:1978 msgid "Area Color" msgstr "Area Color" #: f.tools.cc:2083 f.widgets.cc:519 msgid "Show RGB" msgstr "Show RGB" #: f.tools.cc:2375 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key M to toggle dialog." msgstr "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key M to toggle dialog." #: f.tools.cc:2401 f.widgets.cc:520 msgid "Magnify Image" msgstr "Magnify Image" #: f.tools.cc:2410 msgid "X-size" msgstr "X-size" #: f.tools.cc:2655 msgid "Darkest and Brightest Pixels" msgstr "Darkest and Brightest Pixels" #: f.tools.cc:2678 msgid "Dark Limit" msgstr "Dark Limit" #: f.tools.cc:2679 msgid "Bright Limit" msgstr "Bright Limit" #: f.tools.cc:2774 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:2870 f.widgets.cc:523 msgid "Monitor Gamma" msgstr "Monitor Gamma" #: f.tools.cc:2930 msgid "Available Translations" msgstr "Available Translations" #: f.tools.cc:2934 msgid "Set Language" msgstr "Set Language" #: f.tools.cc:3071 f.widgets.cc:526 msgid "Calibrate Printer" msgstr "Calibrate Printer" #: f.tools.cc:3096 msgid "print color chart" msgstr "print color chart" #: f.tools.cc:3097 msgid "scan and save color chart" msgstr "scan and save color chart" #: f.tools.cc:3098 msgid "align and trim color chart" msgstr "align and trim color chart" #: f.tools.cc:3099 msgid "open and process color chart" msgstr "open and process color chart" #: f.tools.cc:3100 msgid "print image with revised colors" msgstr "print image with revised color" #: f.tools.cc:3264 #, c-format msgid "" "Scan the printed color chart. \n" "Save in %s/" msgstr "" "Scan the printed color chart. \n" "Save in %s/" #: f.tools.cc:3278 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation caused by the scanner. \n" "Trim off all margins. Be careful not to cut off \n" "any color tile edges or leave any white margins." msgstr "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation caused by the scanner. \n" "Trim off all margins. Be careful not to cut off \n" "any color tile edges or leave any white margins." #: f.tools.cc:3310 msgid "Open the trimmed color chart file" msgstr "Open the trimmed color chart file" #: f.tools.cc:3443 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:3483 msgid "Color map file to use" msgstr "Color map file to use" #: f.tools.cc:3500 msgid "Select the image file to print." msgstr "Select the image file to print." #: f.tools.cc:3565 msgid "converting colors..." msgstr "converting colors..." #: f.tools.cc:3665 msgid "Image colors are converted for printing." msgstr "Image colors are converted for printing." #: f.widgets.cc:111 msgid "Album" msgstr "Album" #: f.widgets.cc:113 msgid "TOP" msgstr "TOP" #: f.widgets.cc:165 msgid "Current Image File (key F)" msgstr "Current Image File (key F)" #: f.widgets.cc:166 msgid "Thumbnail Gallery (key G)" msgstr "Thumbnail Gallery (key G)" #: f.widgets.cc:167 msgid "World Maps (key W)" msgstr "World Maps (key W)" #: f.widgets.cc:170 msgid "Favorite Functions" msgstr "Favorite Functions" #: f.widgets.cc:171 msgid "File: Open, RAW, Rename, Trash, Print" msgstr "File: Open, RAW, Rename, Trash, Print" #: f.widgets.cc:172 msgid "Save modified image file to disk" msgstr "Save modified image file to disk" #: f.widgets.cc:173 msgid "Open previous or next file (left/right mouse click)" msgstr "Open previous or next file (left/right mouse click)" #: f.widgets.cc:174 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "Metadata: Captions, Tags, Ratings, Geotags, Search ... " #: f.widgets.cc:175 msgid "Areas: Select areas to edit, copy and paste" msgstr "Areas: Select areas to edit, copy and paste" #: f.widgets.cc:176 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." #: f.widgets.cc:177 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." #: f.widgets.cc:178 msgid "Bend: Fix Perspective, Bend/Warp image ..." msgstr "Bend: Fix Perspective, Bend/Warp image ..." #: f.widgets.cc:179 msgid "Effects: Special Effects, Arty Transforms" msgstr "Effects: Special Effects, Arty Transforms" #: f.widgets.cc:180 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "Combine: HDR, HDF, Panorama, Stack, Mashup" #: f.widgets.cc:181 msgid "" "Undo or Redo one edit (left/right mouse click) \n" " hold key A to include all edits" msgstr "" "Undo or Redo one edit (left/right mouse click) \n" " hold key A to include all edits" #: f.widgets.cc:183 msgid "Tools: Index, Options, Shortcuts, Magnify ..." msgstr "Tools: Index, Options, Shortcuts, Magnify ..." #: f.widgets.cc:184 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "Help: Quick Start, User Guide, Recent Changes ..." #: f.widgets.cc:187 msgid "Set gallery from current image file" msgstr "Set gallery from current image file" #: f.widgets.cc:188 msgid "Albums: Manage Albums, Slide Show, Desktop Wallpaper" msgstr "Albums: Manage Albums, Slide Show, Desktop Wallpaper" #: f.widgets.cc:189 msgid "go to bookmarked image" msgstr "go to bookmarked image" #: f.widgets.cc:190 msgid "increase thumbnail size" msgstr "increase thumbnail size" #: f.widgets.cc:191 msgid "reduce thumbnail size" msgstr "reduce thumbnail size" #: f.widgets.cc:192 msgid "change sort order" msgstr "change sort order" #: f.widgets.cc:193 msgid "jump to beginning (top)" msgstr "jump to beginning (top)" #: f.widgets.cc:194 msgid "jump to end (bottom)" msgstr "jump to end (bottom)" #: f.widgets.cc:195 msgid "previous page" msgstr "previous page" #: f.widgets.cc:196 msgid "next page" msgstr "next page" #: f.widgets.cc:197 msgid "slow scroll" msgstr "slow scroll" #: f.widgets.cc:198 msgid "Batch conversion, metadata, RAW processing" msgstr "Batch conversion, metadata, RAW processing" #: f.widgets.cc:201 msgid "Choose a map for image locations" msgstr "Choose a map for image locations" #: f.widgets.cc:202 msgid "Set image search radius for map click" msgstr "Set image search radius for map click" #: f.widgets.cc:205 msgid "Open another window" msgstr "Open another window" #: f.widgets.cc:206 msgid "Open a new image file" msgstr "Open a new image file" #: f.widgets.cc:207 msgid "Open the previously seen file" msgstr "Open the previously seen file" #: f.widgets.cc:208 msgid "Open a recently seen file" msgstr "Open a recently seen file" #: f.widgets.cc:209 msgid "Open a newly added file" msgstr "Open a newly added file" #: f.widgets.cc:210 f.widgets.cc:211 msgid "Open and edit a camera RAW file" msgstr "Open and edit a camera RAW file" #: f.widgets.cc:212 msgid "Change the image file name" msgstr "Change the image file name" #: f.widgets.cc:213 msgid "Create a blank image" msgstr "Create a blank image" #: f.widgets.cc:214 msgid "Move image file to Trash" msgstr "Move image file to Trash" #: f.widgets.cc:215 msgid "Permanently delete image file" msgstr "Permanently delete image file" #: f.widgets.cc:216 msgid "Print the current image" msgstr "Print the current image" #: f.widgets.cc:217 msgid "Print current image with adjusted colors" msgstr "Print current image with adjusted colors" #: f.widgets.cc:218 f.widgets.cc:406 msgid "Quit Fotoxx" msgstr "Quit Fotoxx" #: f.widgets.cc:221 msgid "List a few key metadata items" msgstr "List a few key metadata items" #: f.widgets.cc:222 msgid "List all metadata items" msgstr "List all metadata items" #: f.widgets.cc:223 msgid "(Toggle) show captions and comments" msgstr "(Toggle) show captions and comments" #: f.widgets.cc:224 msgid "Edit image tags/caption/rating ..." msgstr "Edit image tags/caption/rating ..." #: f.widgets.cc:225 msgid "Edit any image metadata" msgstr "Edit any image metadata" #: f.widgets.cc:226 msgid "Remove all metadata from an image" msgstr "Remove all metadata from an image" #: f.widgets.cc:227 msgid "Add/remove tags for multiple images" msgstr "Add/remove tags for multiple images" #: f.widgets.cc:228 msgid "Convert tag names for all images" msgstr "Convert tag names for all images" #: f.widgets.cc:229 msgid "Add/change/delete metadata for multiple images" msgstr "Add/change/delete metadata for multiple images" #: f.widgets.cc:230 msgid "Edit image location and geotags" msgstr "Edit image location and geotags" #: f.widgets.cc:231 msgid "Add/revise geotags for multiple images" msgstr "Add/revise geotags for multiple images" #: f.widgets.cc:232 msgid "Find all images for a location [date]" msgstr "Find all images for a location [date]" #: f.widgets.cc:233 msgid "Find images meeting select criteria" msgstr "Find images meeting select criteria" #: f.widgets.cc:236 msgid "Select object or area for editing" msgstr "Select object or area for editing" #: f.widgets.cc:237 msgid "Show (outline) existing area" msgstr "Show (outline) existing area" #: f.widgets.cc:238 msgid "Hide existing area" msgstr "Hide existing area" #: f.widgets.cc:239 msgid "Enable area for editing" msgstr "Enable area for editing" #: f.widgets.cc:240 msgid "Disable area for editing" msgstr "Disable area for editing" #: f.widgets.cc:241 msgid "Reverse existing area" msgstr "Reverse existing area" #: f.widgets.cc:242 msgid "Erase existing area" msgstr "Erase existing area" #: f.widgets.cc:243 msgid "Copy area for later pasting into image" msgstr "Copy area for later pasting into image" #: f.widgets.cc:244 msgid "Paste previously copied area into image" msgstr "Paste previously copied area into image" #: f.widgets.cc:245 msgid "Open a file and paste as area into image" msgstr "Open a file and paste as area into image" #: f.widgets.cc:246 msgid "Save area to a file with transparency" msgstr "Save area to a file with transparency" #: f.widgets.cc:249 msgid "Trim/Crop margins and/or Rotate" msgstr "Trim/Crop margins and/or Rotate" #: f.widgets.cc:250 msgid "Upright a rotated image" msgstr "Upright a rotated image" #: f.widgets.cc:251 f.widgets.cc:252 msgid "Fast auto enhance that may work OK" msgstr "Fast auto enhance that may work OK" #: f.widgets.cc:253 msgid "Adjust brightness, contrast, color" msgstr "Adjust brightness, contrast, color" #: f.widgets.cc:254 msgid "Add local contrast, enhance details" msgstr "Add local contrast, enhance details" #: f.widgets.cc:255 msgid "Adjust brightness distribution" msgstr "Adjust brightness distribution" #: f.widgets.cc:256 msgid "Flatten zonal brightness distribution" msgstr "Flatten zonal brightness distribution" #: f.widgets.cc:257 msgid "Change pixel dimensions" msgstr "Change pixel dimensions" #: f.widgets.cc:258 msgid "Mirror image horizontally or vertically" msgstr "Mirror image horizontally or vertically" #: f.widgets.cc:259 msgid "Write text on image" msgstr "Write text on image" #: f.widgets.cc:260 msgid "Write lines or arrows on image" msgstr "Write lines or arrows on image" #: f.widgets.cc:261 msgid "Fix brightness uniformity across image" msgstr "Fix brightness uniformity across image" #: f.widgets.cc:262 msgid "Paint edit function gradually with mouse" msgstr "Paint edit function gradually with mouse" #: f.widgets.cc:263 msgid "Leverage edits by brightness or color" msgstr "Leverage edits by brightness or color" #: f.widgets.cc:264 msgid "Edit plugins menu or run a plugin function" msgstr "Edit plugins menu or run a plugin function" #: f.widgets.cc:267 msgid "Make the image look sharper" msgstr "Make the image look sharper" #: f.widgets.cc:268 msgid "Make the image look fuzzy" msgstr "Make the image look fuzzy" #: f.widgets.cc:269 msgid "Filter noise from low-light photos" msgstr "Filter noise from low-light photos" #: f.widgets.cc:270 msgid "Remove unwanted objects" msgstr "Remove unwanted objects" #: f.widgets.cc:271 msgid "Fix red-eyes from electronic flash" msgstr "Fix red-eyes from electronic flash" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Paint image pixels using the mouse" #: f.widgets.cc:273 msgid "Paint image transparency using the mouse" msgstr "Paint image transparency using the mouse" #: f.widgets.cc:274 msgid "Make BW/color, negative/positive, sepia" msgstr "Make BW/color, negative/positive, sepia" #: f.widgets.cc:275 msgid "Shift/convert colors into other colors" msgstr "Shift/convert colors into other colors" #: f.widgets.cc:276 msgid "Adjust color using RGB or CMY colors" msgstr "Adjust color using RGB or CMY colors" #: f.widgets.cc:277 msgid "Adjust color using HSL colors" msgstr "Adjust color using HSL colors" #: f.widgets.cc:278 msgid "Adjust color in selected image areas" msgstr "Adjust color in selected image areas" #: f.widgets.cc:279 msgid "Match colors on one image with another" msgstr "Match colors on one image with another" #: f.widgets.cc:280 msgid "Convert to another color profile" msgstr "Convert to another color profile" #: f.widgets.cc:281 msgid "Remove dust spots from scanned slides" msgstr "Remove dust spots from scanned slides" #: f.widgets.cc:282 msgid "Smoothen edges with jaggies" msgstr "Smoothen edges with jaggies" #: f.widgets.cc:283 msgid "Reduce Chromatic Abberation" msgstr "Reduce Chromatic Abberation" #: f.widgets.cc:284 msgid "Erase known hot and dark pixels" msgstr "Erase known hot and dark pixels" #: f.widgets.cc:287 msgid "Remove curvature, esp. panoramas" msgstr "Remove curvature, esp. panoramas" #: f.widgets.cc:288 msgid "Straighten objects seen from an angle" msgstr "Straighten objects seen from an angle" #: f.widgets.cc:289 msgid "Distort image areas using the mouse" msgstr "Distort image areas using the mouse" #: f.widgets.cc:290 f.widgets.cc:291 f.widgets.cc:292 msgid "Distort the whole image using the mouse" msgstr "Distort the whole image using the mouse" #: f.widgets.cc:293 msgid "Flatten a photographed book page" msgstr "Flatten a photographed book pag" #: f.widgets.cc:296 msgid "Reduce color depth (posterize)" msgstr "Reduce color depth (posterize)" #: f.widgets.cc:297 msgid "Convert to pencil sketch" msgstr "Convert to pencil sketch" #: f.widgets.cc:298 msgid "Convert to line drawing (edge detection)" msgstr "Convert to line drawing (edge detection)" #: f.widgets.cc:299 msgid "Convert to solid color drawing" msgstr "Convert to solid color drawing" #: f.widgets.cc:300 msgid "Graduated Blur depending on contrast" msgstr "Graduated Blur depending on contrast" #: f.widgets.cc:301 msgid "Create an embossed or 3D appearance" msgstr "Create an embossed or 3D appearance" #: f.widgets.cc:302 msgid "Convert to square tiles" msgstr "Convert to square tiles" #: f.widgets.cc:303 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "Convert to dots (Roy Lichtenstein effect)" #: f.widgets.cc:304 msgid "Convert into a simulated painting" msgstr "Convert into a simulated painting" #: f.widgets.cc:305 msgid "Change brightness or color radially" msgstr "Change brightness or color radially" #: f.widgets.cc:306 msgid "Add texture to an image" msgstr "Add texture to an image" #: f.widgets.cc:307 msgid "Tile image with a repeating pattern" msgstr "Tile image with a repeating pattern" #: f.widgets.cc:308 msgid "Create a mosaic with tiles made from all images" msgstr "Create a mosaic with tiles made from all images" #: f.widgets.cc:309 msgid "Process an image using a custom kernel" msgstr "Process an image using a custom kernel" #: f.widgets.cc:310 msgid "Warp an image with a wave pattern" msgstr "Warp an image with a wave pattern" #: f.widgets.cc:311 msgid "Blur an image in the direction of mouse drags" msgstr "Blur an image in the direction of mouse drags" #: f.widgets.cc:312 msgid "Make a spherical projection of an image" msgstr "Make a spherical projection of an image" #: f.widgets.cc:315 msgid "Combine bright/dark images for better detail" msgstr "Combine bright/dark images for better detail" #: f.widgets.cc:316 msgid "Combine near/far focus images for deeper focus" msgstr "Combine near/far focus images for deeper focus" #: f.widgets.cc:317 msgid "Combine images to erase passing people, etc." msgstr "Combine images to erase passing people, etc." #: f.widgets.cc:318 msgid "Combine noisy images into a low-noise image" msgstr "Combine noisy images into a low-noise image" #: f.widgets.cc:319 msgid "Combine images into a panorama" msgstr "Combine images into a panorama" #: f.widgets.cc:320 msgid "Combine images into a vertical panorama" msgstr "Combine images into a vertical panorama" #: f.widgets.cc:321 msgid "Combine images into a panorama (panorama tools)" msgstr "Combine images into a panorama (panorama tools)" #: f.widgets.cc:322 msgid "Arrange images and text in a layout (montage)" msgstr "Arrange images and text in a layout (montage)" #: f.widgets.cc:325 msgid "Index new files and make thumbnails" msgstr "Index new files and make thumbnails" #: f.widgets.cc:326 msgid "Change user preferences" msgstr "Change user preferences" #: f.widgets.cc:327 msgid "Change Keyboard Shortcut Keys" msgstr "Change Keyboard Shortcut Keys" #: f.widgets.cc:328 msgid "Show a brightness distribution graph" msgstr "Show a brightness distribution graph" #: f.widgets.cc:329 msgid "Show or revise grid lines" msgstr "Show or revise grid lines" #: f.widgets.cc:330 msgid "Change color of foreground lines" msgstr "Change color of foreground lines" #: f.widgets.cc:331 msgid "Show RGB colors at mouse click" msgstr "Show RGB colors at mouse click" #: f.widgets.cc:332 msgid "Magnify image around the mouse position" msgstr "Magnify image around the mouse position" #: f.widgets.cc:333 msgid "Highlight darkest and brightest pixels" msgstr "Highlight darkest and brightest pixels" #: f.widgets.cc:334 msgid "Chart to adjust monitor color" msgstr "Chart to adjust monitor color" #: f.widgets.cc:335 msgid "Chart to adjust monitor gamma" msgstr "Chart to adjust monitor gamma" #: f.widgets.cc:336 msgid "Change the GUI language" msgstr "Change the GUI language" #: f.widgets.cc:337 msgid "Report missing translations" msgstr "Report missing translations" #: f.widgets.cc:338 msgid "Calibrate printer colors" msgstr "Calibrate printer colors" #: f.widgets.cc:339 msgid "Memory and CPU (to terminal/logfile)" msgstr "Memory and CPU (to terminal/logfile" #: f.widgets.cc:343 msgid "Rename/convert/resize/move multiple files" msgstr "Rename/convert/resize/move multiple files" #: f.widgets.cc:344 msgid "Upright multiple rotated image files" msgstr "Upright multiple rotated image files" #: f.widgets.cc:345 msgid "Delete or Trash multiple files" msgstr "Delete or Trash multiple files" #: f.widgets.cc:346 msgid "Convert camera RAW files using DCraw" msgstr "Convert camera RAW files using DCraw" #: f.widgets.cc:347 msgid "Convert camera RAW files using Raw Therapee" msgstr "Convert camera RAW files using Raw Therapee" #: f.widgets.cc:348 msgid "Build and run edit script files" msgstr "Build and run edit script files" #: f.widgets.cc:349 msgid "Burn selected image files to CD or DVD" msgstr "Burn selected image files to CD or DVD" #: f.widgets.cc:350 msgid "Search all image files and report duplicates" msgstr "Search all image files and report duplicates" #: f.widgets.cc:354 msgid "Quick Start mini-guide" msgstr "Quick Start mini-guide" #: f.widgets.cc:355 msgid "Read the user guide" msgstr "Read the user guide" #: f.widgets.cc:356 msgid "Recent user guide changes" msgstr "Recent user guide changes" #: f.widgets.cc:357 msgid "Summary of image edit functions" msgstr "Summary of image edit functions" #: f.widgets.cc:358 msgid "Technical installation notes" msgstr "Technical installation notes" #: f.widgets.cc:359 msgid "List updates by Fotoxx version" msgstr "List updates by Fotoxx version" #: f.widgets.cc:360 msgid "View the log file and error messages" msgstr "View the log file and error messages" #: f.widgets.cc:361 msgid "How to do Fotoxx translations" msgstr "How to do Fotoxx translations" #: f.widgets.cc:362 msgid "Show the Fotoxx web page" msgstr "Show the Fotoxx web page" #: f.widgets.cc:363 msgid "Version, license, contact, credits" msgstr "Version, license, contact, credits" #: f.widgets.cc:366 msgid "Organize images into albums" msgstr "Organize images into albums" #: f.widgets.cc:367 msgid "Start a slide show" msgstr "Start a slide show" #: f.widgets.cc:368 msgid "Set desktop wallpaper from current Fotoxx image" msgstr "Set desktop wallpaper from current Fotoxx image" #: f.widgets.cc:369 msgid "Cycle desktop wallpaper from a Fotoxx album" msgstr "Cycle desktop wallpaper from a Fotoxx album" #: f.widgets.cc:391 msgid "New Window" msgstr "New Window" #: f.widgets.cc:392 msgid "Sync Gallery" msgstr "Sync Gallery" #: f.widgets.cc:393 msgid "Recently Seen Images" msgstr "Recently Seen Images" #: f.widgets.cc:394 msgid "Newest Images" msgstr "Newest Images" #: f.widgets.cc:396 msgid "Open Previous File" msgstr "Open Previous File" #: f.widgets.cc:397 msgid "Open RAW (ufraw)" msgstr "Open RAW (ufraw)" #: f.widgets.cc:398 msgid "Open RAW (Raw Therapee)" msgstr "Open RAW (Raw Therapee" #: f.widgets.cc:399 msgid "New Blank Image" msgstr "New Blank Image" #: f.widgets.cc:402 msgid "Delete Image File" msgstr "Delete Image File" #: f.widgets.cc:403 msgid "Print Image" msgstr "Print Image" #: f.widgets.cc:404 msgid "Print Calibrated Image" msgstr "Print Calibrated Image" #: f.widgets.cc:405 f.widgets.cc:550 msgid "Set Desktop Wallpaper" msgstr "Set Desktop Wallpaper" #: f.widgets.cc:409 msgid "View Metadata (short)" msgstr "View Metadata (short)" #: f.widgets.cc:410 msgid "View Metadata (long)" msgstr "View Metadata (long)" #: f.widgets.cc:411 msgid "Show Captions on Image" msgstr "Show Captions on Image" #: f.widgets.cc:413 msgid "Edit Any Metadata" msgstr "Edit Any Metadata" #: f.widgets.cc:416 f.widgets.cc:541 msgid "Batch Rename Tags" msgstr "Batch Rename Tags" #: f.widgets.cc:417 f.widgets.cc:542 msgid "Batch Add/Change Metadata" msgstr "Batch Add/Change Metadata" #: f.widgets.cc:420 f.widgets.cc:544 f.widgets.cc:618 msgid "Images by Geotag" msgstr "Images by Geotag" #: f.widgets.cc:421 f.widgets.cc:545 f.widgets.cc:619 msgid "Search Images" msgstr "Search Images" #: f.widgets.cc:424 fotoxx.h:1168 msgid "Select" msgstr "Select" #: f.widgets.cc:425 fotoxx.h:1170 msgid "Show" msgstr "Show" #: f.widgets.cc:426 fotoxx.h:1124 msgid "Hide" msgstr "Hide" #: f.widgets.cc:427 fotoxx.h:1110 msgid "Enable" msgstr "Enable" #: f.widgets.cc:428 fotoxx.h:1106 msgid "Disable" msgstr "Disable" #: f.widgets.cc:429 fotoxx.h:1127 msgid "Invert" msgstr "Invert" #: f.widgets.cc:430 fotoxx.h:1184 msgid "Unselect" msgstr "Unselect" #: f.widgets.cc:431 msgid "Copy Area" msgstr "Copy Area" #: f.widgets.cc:432 msgid "Paste Area" msgstr "Paste Area" #: f.widgets.cc:433 msgid "Open Area File" msgstr "Open Area File" #: f.widgets.cc:434 msgid "Save Area File" msgstr "Save Area File" #: f.widgets.cc:439 msgid "Voodoo 1" msgstr "Voodoo 1" #: f.widgets.cc:440 msgid "Voodoo 2" msgstr "Voodoo 2" #: f.widgets.cc:442 f.widgets.cc:687 msgid "Brightness Dist." msgstr "Brightness Dist." #: f.widgets.cc:443 f.widgets.cc:688 msgid "Zonal Flatten" msgstr "Zonal Flatten" #: f.widgets.cc:447 msgid "Add Text" msgstr "Add Text" #: f.widgets.cc:448 msgid "Add Lines" msgstr "Add Lines" #: f.widgets.cc:451 msgid "Plugins" msgstr "Plugins" #: f.widgets.cc:456 msgid "Denoise" msgstr "Denoise" #: f.widgets.cc:458 msgid "Red Eyes" msgstr "Red Eyes" #: f.widgets.cc:463 msgid "Adjust RGB/CMY" msgstr "Adjust RGB/CMY" #: f.widgets.cc:464 msgid "Adjust HSL" msgstr "Adjust HSL" #: f.widgets.cc:465 msgid "Brightness Ramp" msgstr "Brightness Ramp" #: f.widgets.cc:467 msgid "Match Colors" msgstr "Match Colors" #: f.widgets.cc:468 msgid "Color Profile" msgstr "Color Profile" #: f.widgets.cc:470 msgid "Anti-Alias" msgstr "Anti-Alias" #: f.widgets.cc:476 msgid "Fix Perspective" msgstr "Fix Perspective" #: f.widgets.cc:481 msgid "Flatten Book Page" msgstr "Flatten Book Page" #: f.widgets.cc:484 msgid "Color Depth" msgstr "Color Depth" #: f.widgets.cc:485 msgid "Sketch" msgstr "Sketch" #: f.widgets.cc:489 msgid "Embossing" msgstr "Embossing" #: f.widgets.cc:491 msgid "Dots" msgstr "Dots" #: f.widgets.cc:492 msgid "Painting" msgstr "Painting" #: f.widgets.cc:494 msgid "Texture" msgstr "Texture" #: f.widgets.cc:496 msgid "Mosaic" msgstr "Mosaic" #: f.widgets.cc:503 msgid "High Dynamic Range" msgstr "High Dynamic Range" #: f.widgets.cc:504 msgid "High Depth of Field" msgstr "High Depth of Field" #: f.widgets.cc:505 msgid "Stack/Paint" msgstr "Stack/Paint" #: f.widgets.cc:506 msgid "Stack/Noise" msgstr "Stack/Noise" #: f.widgets.cc:507 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:508 msgid "Vertical Panorama" msgstr "Vertical Panorama" #: f.widgets.cc:509 msgid "PT Panorama" msgstr "PT Panorama" #: f.widgets.cc:510 msgid "Mashup" msgstr "Mashup" #: f.widgets.cc:515 msgid "Keyboard Shortcuts" msgstr "Keyboard Shortcuts" #: f.widgets.cc:516 msgid "Show Brightness Dist." msgstr "Show Brightness Dist." #: f.widgets.cc:521 msgid "Dark/Bright Pixels" msgstr "Dark/Bright Pixels" #: f.widgets.cc:522 msgid "Monitor Color" msgstr "Monitor Color" #: f.widgets.cc:524 msgid "Change Language" msgstr "Change Language" #: f.widgets.cc:525 msgid "Missing Translations" msgstr "Missing Translations" #: f.widgets.cc:527 msgid "Resources" msgstr "Resources" #: f.widgets.cc:534 msgid "Batch RAW (DCraw)" msgstr "Batch RAW (DCraw)" #: f.widgets.cc:535 msgid "Batch RAW (Raw Therapee)" msgstr "Batch RAW (Raw Therapee)" #: f.widgets.cc:536 msgid "Script Files" msgstr "Script Files" #: f.widgets.cc:537 msgid "Burn Images to CD/DVD" msgstr "Burn Images to CD/DVD" #: f.widgets.cc:551 msgid "Cycle Desktop Wallpaper" msgstr "Cycle Desktop Wallpaper" #: f.widgets.cc:568 f.widgets.cc:591 f.widgets.cc:613 msgid "Gallery" msgstr "Gallery" #: f.widgets.cc:569 f.widgets.cc:592 f.widgets.cc:614 msgid "World Maps" msgstr "World Maps" #: f.widgets.cc:571 msgid "Favorites" msgstr "Favorites" #: f.widgets.cc:573 fotoxx.h:1164 msgid "Save" msgstr "Save" #: f.widgets.cc:574 msgid "Prev/Next" msgstr "Prev/Next" #: f.widgets.cc:575 msgid "Metadata" msgstr "Metadata" #: f.widgets.cc:576 msgid "Areas" msgstr "Areas" #: f.widgets.cc:577 fotoxx.h:1109 msgid "Edit" msgstr "Edit" #: f.widgets.cc:578 msgid "Repair" msgstr "Repair" #: f.widgets.cc:579 msgid "Bend" msgstr "Bend" #: f.widgets.cc:580 msgid "Effects" msgstr "Effects" #: f.widgets.cc:581 msgid "Combine" msgstr "Combine" #: f.widgets.cc:582 msgid "Undo/Redo" msgstr "Undo/Redo" #: f.widgets.cc:583 msgid "Tools" msgstr "Tools" #: f.widgets.cc:594 msgid "Sync.G" msgstr "Sync.G" #: f.widgets.cc:595 msgid "Albums" msgstr "Albums" #: f.widgets.cc:596 msgid "Bookmarks" msgstr "Bookmarks" #: f.widgets.cc:605 msgid "Batch" msgstr "Batch" #: f.widgets.cc:616 msgid "Choose Map" msgstr "Choose Map" #: f.widgets.cc:617 msgid "Search Range" msgstr "Search Range" #: f.widgets.cc:625 fotoxx.h:1182 msgid "Undo" msgstr "Undo" #: f.widgets.cc:626 fotoxx.h:1155 msgid "Redo" msgstr "Redo" #: f.widgets.cc:667 msgid "Popup Image" msgstr "Popup Image" #: f.widgets.cc:668 msgid "Popup Image (add)" msgstr "Popup Image (add)" #: f.widgets.cc:672 fotoxx.h:1158 msgid "Rename" msgstr "Rename" #: f.widgets.cc:673 msgid "Copy to Location" msgstr "Copy to Location" #: f.widgets.cc:674 msgid "Move to Location" msgstr "Move to Location" #: f.widgets.cc:675 msgid "Copy to Clipboard" msgstr "Copy to Clipboard" #: f.widgets.cc:676 msgid "Remove from Album" msgstr "Remove from Album" #: f.widgets.cc:677 msgid "Cut to Image Cache" msgstr "Cut to Image Cache" #: f.widgets.cc:678 msgid "Copy to Image Cache" msgstr "Copy to Image Cache" #: f.widgets.cc:679 msgid "Paste Image Cache Here (clear)" msgstr "Paste Image Cache Here (clear)" #: f.widgets.cc:680 msgid "Paste Image Cache Here (keep)" msgstr "Paste Image Cache Here (keep)" #: f.widgets.cc:681 msgid "Paste Current Image File Here" msgstr "Paste Current Image File Here" #: f.widgets.cc:685 msgid "Voodoo Enhance" msgstr "Voodoo Enhance" #: f.widgets.cc:690 msgid "Select Area" msgstr "Select Area" #: f.widgets.cc:693 fotoxx.h:1178 msgid "Trash" msgstr "Trash" #: f.widgets.cc:819 msgid "Image Locations" msgstr "Image Locations" #: fotoxx-15.11.cc:373 msgid "Please install missing programs:" msgstr "Please install missing programs:" #: fotoxx-15.11.cc:662 msgid "Kill active dialog?" msgstr "Kill active dialog?" #: fotoxx-15.11.cc:733 msgid "(reduced)" msgstr "(reduced" #: fotoxx-15.11.cc:734 msgid "area active" msgstr "area active" #: fotoxx-15.11.cc:735 msgid "dialog open" msgstr "dialog open" #: fotoxx-15.11.cc:736 msgid "blocked" msgstr "blocked" #: fotoxx-15.11.cc:786 msgid "edits" msgstr "edits" #: fotoxx-15.11.cc:2793 msgid "Exceed 50 anchor points" msgstr "Exceed 50 anchor points" #: fotoxx-15.11.cc:3013 msgid "load curve from a file" msgstr "load curve from a file" #: fotoxx-15.11.cc:3079 msgid "curve file is invalid" msgstr "curve file is invalid" #: fotoxx-15.11.cc:3093 msgid "save curve to a file" msgstr "save curve to a file" #: fotoxx-15.11.cc:3165 msgid "Too many edits, please save image" msgstr "Too many edits, please save image" #: fotoxx-15.11.cc:3170 msgid "this function cannot be scripted" msgstr "this function cannot be scripted" #: fotoxx-15.11.cc:3187 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Select area not active.\n" "Continue?" #: fotoxx-15.11.cc:3499 msgid "file data does not fit dialog" msgstr "file data does not fit dialog" #: fotoxx-15.11.cc:3509 msgid "Save settings to a file" msgstr "Save settings to a file" #: fotoxx-15.11.cc:3825 msgid "This action will discard changes to current image" msgstr "This action will discard changes to current image" #: fotoxx-15.11.cc:3826 msgid "prior function still active" msgstr "prior function still active" #: fotoxx-15.11.cc:3827 fotoxx.h:1128 msgid "Keep" msgstr "Keep" #: fotoxx-15.11.cc:3828 msgid "Discard" msgstr "Discard" #: fotoxx.h:1080 msgid "Add" msgstr "Add" #: fotoxx.h:1081 msgid "Add All" msgstr "Add All" #: fotoxx.h:1083 msgid "Amount" msgstr "Amount" #: fotoxx.h:1084 msgid "Angle" msgstr "Angle" #: fotoxx.h:1085 msgid "Apply" msgstr "Apply" #: fotoxx.h:1086 msgid "Auto" msgstr "Auto" #: fotoxx.h:1087 msgid "Black" msgstr "Black" #: fotoxx.h:1088 msgid "Blend Width" msgstr "Blend Width" #: fotoxx.h:1090 zfuncs.cc:10098 msgid "bottom" msgstr "bottom" #: fotoxx.h:1092 msgid "Browse" msgstr "Browse" #: fotoxx.h:1093 msgid "Cancel" msgstr "Cancel" #: fotoxx.h:1094 msgid "Center" msgstr "Center" #: fotoxx.h:1095 msgid "Choose" msgstr "Choose" #: fotoxx.h:1096 msgid "Clear" msgstr "Clear" #: fotoxx.h:1097 msgid "Color" msgstr "Color" #: fotoxx.h:1098 msgid "continue" msgstr "continue" #: fotoxx.h:1100 msgid "Copy" msgstr "Copy" #: fotoxx.h:1101 msgid "Create" msgstr "Create" #: fotoxx.h:1102 msgid "Curve File:" msgstr "Curve File:" #: fotoxx.h:1103 msgid "Cut" msgstr "Cut" #: fotoxx.h:1104 msgid "Deband" msgstr "Deband" #: fotoxx.h:1105 msgid "Delete" msgstr "Delete" #: fotoxx.h:1107 msgid "Done" msgstr "Done" #: fotoxx.h:1108 msgid "edge" msgstr "edge" #: fotoxx.h:1111 msgid "Erase" msgstr "Erase" #: fotoxx.h:1112 msgid "Fetch" msgstr "Fetch" #: fotoxx.h:1113 msgid "output file already exists" msgstr "output file already exists" #: fotoxx.h:1114 #, c-format msgid "%d files selected" msgstr "%d files selected" #: fotoxx.h:1115 msgid "Find" msgstr "Find" #: fotoxx.h:1116 msgid "Finish" msgstr "Finish" #: fotoxx.h:1117 msgid "Flatten" msgstr "Flatten" #: fotoxx.h:1118 msgid "Font" msgstr "Font" #: fotoxx.h:1119 msgid "Geotags" msgstr "Geotags" #: fotoxx.h:1121 msgid "Grid" msgstr "Grid" #: fotoxx.h:1122 msgid "Height" msgstr "Height" #: fotoxx.h:1125 msgid "Images" msgstr "Images" #: fotoxx.h:1126 msgid "Insert" msgstr "Insert" #: fotoxx.h:1129 zfuncs.cc:10102 msgid "left" msgstr "left" #: fotoxx.h:1130 msgid "limit" msgstr "limit" #: fotoxx.h:1131 msgid "Load" msgstr "Load" #: fotoxx.h:1132 msgid "Make" msgstr "Make" #: fotoxx.h:1134 msgid "Map" msgstr "Map" #: fotoxx.h:1135 msgid "Max" msgstr "Max" #: fotoxx.h:1136 msgid "Negative" msgstr "Negative" #: fotoxx.h:1137 msgid "New" msgstr "New" #: fotoxx.h:1138 msgid "Next" msgstr "Next" #: fotoxx.h:1139 zfuncs.cc:9190 msgid "No" msgstr "No" #: fotoxx.h:1140 msgid "no images" msgstr "no images" #: fotoxx.h:1142 msgid "no selection" msgstr "no selection" #: fotoxx.h:1143 msgid "OK" msgstr "OK" #: fotoxx.h:1145 msgid "Paste" msgstr "Paste" #: fotoxx.h:1146 msgid "Pause" msgstr "Pause" #: fotoxx.h:1147 msgid "Percent" msgstr "Percent" #: fotoxx.h:1149 msgid "Presets" msgstr "Presets" #: fotoxx.h:1150 msgid "Prev" msgstr "Prev" #: fotoxx.h:1151 msgid "Proceed" msgstr "Proceed" #: fotoxx.h:1153 msgid "range" msgstr "range" #: fotoxx.h:1156 msgid "Reduce" msgstr "Reduce" #: fotoxx.h:1157 msgid "Remove" msgstr "Remove" #: fotoxx.h:1159 msgid "Replace" msgstr "Replace" #: fotoxx.h:1160 msgid "Reserved" msgstr "Reserved" #: fotoxx.h:1161 msgid "Reset" msgstr "Reset" #: fotoxx.h:1162 zfuncs.cc:10106 msgid "right" msgstr "right" #: fotoxx.h:1163 msgid "Rotate" msgstr "Rotate" #: fotoxx.h:1165 msgid "Unknown file type, save as tiff/jpeg/png to edit" msgstr "Unknown file type, save as tiff/jpeg/png to edit" #: fotoxx.h:1166 msgid "Search" msgstr "Search" #: fotoxx.h:1167 msgid "Seconds" msgstr "Seconds" #: fotoxx.h:1171 msgid "Size" msgstr "Size" #: fotoxx.h:1172 msgid "Start" msgstr "Start" #: fotoxx.h:1173 msgid "Strength" msgstr "Strength" #: fotoxx.h:1174 msgid "Threshold" msgstr "Threshold" #: fotoxx.h:1175 #, c-format msgid "exceed %d files" msgstr "exceed %d files" #: fotoxx.h:1176 zfuncs.cc:10094 msgid "top" msgstr "top" #: fotoxx.h:1177 msgid "Transparency" msgstr "Transparency" #: fotoxx.h:1179 msgid "Trim" msgstr "Trim" #: fotoxx.h:1180 msgid "Undo All" msgstr "Undo All" #: fotoxx.h:1181 msgid "Undo Last" msgstr "Undo Last" #: fotoxx.h:1183 msgid "Unfinish" msgstr "Unfinish" #: fotoxx.h:1185 msgid "View" msgstr "View" #: fotoxx.h:1186 msgid "Web" msgstr "Web" #: fotoxx.h:1187 msgid "White" msgstr "White" #: fotoxx.h:1188 msgid "Width" msgstr "Width" #: fotoxx.h:1189 msgid "x-offset" msgstr "x-offset" #: fotoxx.h:1190 msgid "y-offset" msgstr "y-offset" #: fotoxx.h:1191 zfuncs.cc:9190 msgid "Yes" msgstr "Yes" #: zfuncs.cc:1601 #, c-format msgid "" "create directory? \n" " %s" msgstr "" "create directory? \n" " %s" #: zfuncs.cc:4378 msgid "user guide not found" msgstr "user guide not found" #: zfuncs.cc:5196 #, c-format msgid "cannot open file %s" msgstr "cannot open file %s" #: zfuncs.cc:5232 msgid "save screen to file" msgstr "save screen to file" #: zfuncs.cc:9266 zfuncs.cc:9761 zfuncs.cc:10081 msgid "cancel" msgstr "cancel" #: zfuncs.cc:9722 msgid "choose file" msgstr "choose file" #: zfuncs.cc:9727 msgid "choose files" msgstr "choose files" #: zfuncs.cc:9732 msgid "save" msgstr "save" #: zfuncs.cc:9738 msgid "choose folder" msgstr "choose folder" #: zfuncs.cc:9743 msgid "choose folders" msgstr "choose folders" #: zfuncs.cc:9748 msgid "create folder" msgstr "create folder" #: zfuncs.cc:9755 msgid "hidden" msgstr "hidden" #: zfuncs.cc:10081 msgid "done" msgstr "done" #: zfuncs.cc:10081 zfuncs.cc:10090 msgid "margins" msgstr "margins" #: zfuncs.cc:10111 msgid "image scale" msgstr "image scale" #: zfuncs.cc:10113 msgid "percent" msgstr "percent" #: zfuncs.cc:10120 msgid "image" msgstr "image" #: zfuncs.cc:10124 msgid "width" msgstr "width" #: zfuncs.cc:10128 msgid "height" msgstr "height" fotoxx-15.11.1/locales/translate-ru.po0000664000175000017500000035451112616075370016274 0ustar micomicomsgid "" msgstr "" "Project-Id-Version: fotoxx-13.08.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-10-28 22:08+0100\n" "PO-Revision-Date: 2014-09-29 15:54+0300\n" "Last-Translator: Nikolay Kachulin \n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.5.5\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" #: f.albums.cc:78 msgid "" "Right-click album thumbnail to cut/copy \n" "to cache, insert from cache, or remove." msgstr "" "Правый щелчок по иконке альбома чтобы \n" "вырезать/копировать/вставить в кэш или удалить" #: f.albums.cc:80 msgid "Drag album thumbnail to new position." msgstr "Перетащите иконку альбома в новое место" #: f.albums.cc:109 f.widgets.cc:548 msgid "Manage Albums" msgstr "Управление альбомами" #: f.albums.cc:120 f.albums.cc:258 msgid "Create or replace an album" msgstr "Создать/заместить альбом" #: f.albums.cc:122 msgid "Album to view or edit" msgstr "Альбом для просмотра/редактирования" #: f.albums.cc:124 msgid "Select images, add to cache" msgstr "Выбрать изображения, добавить в кэш" #: f.albums.cc:126 msgid "Clear image cache" msgstr "Очистить кэш изображений" #: f.albums.cc:128 f.albums.cc:150 msgid "Delete an album" msgstr "Удалить альбом" #: f.albums.cc:149 msgid "Choose Album" msgstr "Выбрать альбом" #: f.albums.cc:151 #, c-format msgid "Image cache has %d images" msgstr "" #: f.albums.cc:152 msgid "cache added to empty album" msgstr "кэш добавлен к пустому альбому" #: f.albums.cc:207 #, c-format msgid "delete %s ?" msgstr "Удалить %s ?" #: f.albums.cc:242 #, c-format msgid "fill from image cache (%d images)" msgstr "заполнение из кэша (%d изображений)" #: f.albums.cc:260 f.albums.cc:297 msgid "Album Name" msgstr "Имя Альбома" #: f.albums.cc:266 msgid "make an initially empty album" msgstr "создание пустого альбома" #: f.albums.cc:268 msgid "fill from current gallery" msgstr "заполнение из текущей галереи" #: f.albums.cc:311 msgid "enter an album name" msgstr "ввод имени альбома" #: f.albums.cc:338 msgid "gallery is empty" msgstr "галерея пуста" #: f.albums.cc:365 msgid "new album created" msgstr "создан новый альбом" #: f.albums.cc:1099 msgid "Press ESC to exit slide show" msgstr "Нажмите ESC для выхода из слайд-шоу" #: f.albums.cc:1101 msgid "instant" msgstr "Немедленно" #: f.albums.cc:1102 msgid "fade-in" msgstr "Проявка" #: f.albums.cc:1103 msgid "roll-right" msgstr "Скатить вправо" #: f.albums.cc:1104 msgid "roll-down" msgstr "Скатить вниз" #: f.albums.cc:1105 msgid "venetian" msgstr "Жалюзи" #: f.albums.cc:1106 msgid "grate" msgstr "Решетка" #: f.albums.cc:1107 msgid "rectangle" msgstr "Прямоугольник" #: f.albums.cc:1108 msgid "implode" msgstr "" #: f.albums.cc:1109 msgid "explode" msgstr "" #: f.albums.cc:1110 msgid "radar" msgstr "Радар" #: f.albums.cc:1111 msgid "Japanese-fan" msgstr "" #: f.albums.cc:1112 msgid "jaws" msgstr "Челюсти" #: f.albums.cc:1113 msgid "ellipse" msgstr "Эллипс" #: f.albums.cc:1114 msgid "raindrops" msgstr "дождевые капли" #: f.albums.cc:1115 msgid "doubledoor" msgstr "" #: f.albums.cc:1116 msgid "rotate" msgstr "поворот" #: f.albums.cc:1117 msgid "fallover" msgstr "" #: f.albums.cc:1118 msgid "sphereoid" msgstr "" #: f.albums.cc:1139 f.widgets.cc:549 msgid "Slide Show" msgstr "Слайд-шоу" #: f.albums.cc:1150 msgid "Clip Limit" msgstr "возможной обрезки" #: f.albums.cc:1154 msgid "Music File" msgstr "Музыкальный файл" #: f.albums.cc:1159 msgid "Full Screen" msgstr "На Весь Экран" #: f.albums.cc:1160 msgid "Auto-replay" msgstr "Автоповтор" #: f.albums.cc:1166 msgid "Customize:" msgstr "Настроить:" #: f.albums.cc:1167 msgid "transitions" msgstr "Переходы" #: f.albums.cc:1168 msgid "image files" msgstr "файлы изображений" #: f.albums.cc:1173 f.albums.cc:1289 #, c-format msgid "%d images" msgstr "%d изображений" #: f.albums.cc:1195 f.albums.cc:1259 f.albums.cc:1349 f.albums.cc:1462 #: f.albums.cc:1655 f.albums.cc:1739 f.albums.cc:1756 f.albums.cc:1977 msgid "invalid album" msgstr "несуществующий альбом" #: f.albums.cc:1273 msgid "open album" msgstr "открыть альбом" #: f.albums.cc:1305 msgid "Select music file" msgstr "Выберите музыкальный файл" #: f.albums.cc:1329 msgid "select random (if 5+ enabled)" msgstr "произвольный выбор (если отмечено >5)" #: f.albums.cc:1353 msgid "Transition Preferences" msgstr "Настройка Переходов" #: f.albums.cc:1362 msgid "transition" msgstr "переход" #: f.albums.cc:1363 msgid "enabled" msgstr "выбран" #: f.albums.cc:1364 msgid "slowdown" msgstr "замедление" #: f.albums.cc:1365 msgid "preference" msgstr "удельный вес" #: f.albums.cc:1468 msgid "Image Preferences" msgstr "Настройки Изображения" #: f.albums.cc:1472 f.file.cc:1159 f.file.cc:1400 f.file.cc:1608 f.meta.cc:437 msgid "Image File:" msgstr "Файл изображения:" #: f.albums.cc:1476 msgid "Play tone when image shows" msgstr "" #: f.albums.cc:1479 msgid "Show image caption" msgstr "" #: f.albums.cc:1484 msgid "Show image comments" msgstr "" #: f.albums.cc:1489 msgid "Wait before zoom" msgstr "" #: f.albums.cc:1494 msgid "Zoom type:" msgstr "" #: f.albums.cc:1495 msgid "none" msgstr "" #: f.albums.cc:1496 msgid "zoom-in" msgstr "" #: f.albums.cc:1497 msgid "zoom-out" msgstr "" #: f.albums.cc:1500 msgid "Zoom size (x)" msgstr "Увеличение (х)" #: f.albums.cc:1503 msgid "Steps" msgstr "Шагов" #: f.albums.cc:1507 msgid "Zoom image location:" msgstr "" #: f.albums.cc:1515 msgid "Wait after zoom" msgstr "" #: f.albums.cc:1520 msgid "Transition to next image" msgstr "" #: f.albums.cc:1523 f.albums.cc:1625 msgid "next" msgstr "cледующий" #: f.albums.cc:1962 f.albums.cc:2120 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "ошибка формата файла: \n" " %s" #: f.area.cc:79 msgid "Select Area for Edits" msgstr "Выберите область редактирования" #: f.area.cc:80 f.edit.cc:5927 msgid "Press F1 for help" msgstr "F1 для вызова справки" #: f.area.cc:89 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Выбранная область не поддерживается \n" "функцией редактирования" #: f.area.cc:119 msgid "select rectangle" msgstr "Выбрать прямоугольник" #: f.area.cc:120 msgid "select ellipse" msgstr "Выбрать эллипс" #: f.area.cc:123 msgid "freehand draw" msgstr "Рисовать от руки" #: f.area.cc:124 msgid "follow edge" msgstr "Контрастная граница" #: f.area.cc:125 msgid "draw/replace" msgstr "провести/заменить" #: f.area.cc:128 msgid "select area within mouse" msgstr "Выбрать область внутри пятна" #: f.area.cc:131 msgid "select one matching color within mouse" msgstr "Выбрать один цвет внутри пятна" #: f.area.cc:135 msgid "select all matching colors within mouse" msgstr "выделить все совпадающие цвета вокруг мыши" #: f.area.cc:139 f.edit.cc:5972 msgid "mouse radius" msgstr "Радиус пятна" #: f.area.cc:142 msgid "match level %" msgstr "Совпадение цвета %" #: f.area.cc:147 msgid "search range" msgstr "Диапазон поиска" #: f.area.cc:149 msgid "firewall" msgstr "Внутри области" #: f.area.cc:152 msgid "Area Edge Blend Width" msgstr "Ширина Смешивания Краёв Области" #: f.area.cc:155 msgid "Edge Creep" msgstr "Сдвиг Границы" #: f.area.cc:160 msgid "Line Color:" msgstr "" #: f.area.cc:180 f.area.cc:181 msgid "area edits fade away within edge distance" msgstr "плавность перехода для избегания видимых швов " #: f.area.cc:360 f.area.cc:543 #, c-format msgid "exceed %d edits" msgstr "Лимит %d редакций" #: f.area.cc:1364 msgid "" "Click one time inside each enclosed area \n" "(possible gaps in the outline will be found). \n" "Press F1 for help." msgstr "" "Кликните один раз в каждой замкнутой области \n" "(будут найдены возможные разрывы контура). \n" "F1 для вызова справки." #: f.area.cc:1392 msgid "finish area" msgstr "Замкнуть область" #: f.area.cc:1400 msgid "extend to corner:" msgstr "расширить до угла" #: f.area.cc:1441 msgid "searching" msgstr "Поиск" #: f.area.cc:1472 msgid "outline has a gap" msgstr "Контур имеет разрыв" #: f.area.cc:1476 msgid "success" msgstr "Успешно" #: f.area.cc:1613 f.area.cc:1640 #, c-format msgid "found %d pixels" msgstr "Найдено %d пикселей" #: f.area.cc:1935 f.area.cc:1952 f.area.cc:1972 f.area.cc:2287 f.area.cc:2363 #: f.area.cc:2520 msgid "the area is not finished" msgstr "Область не замкнута" #: f.area.cc:2061 msgid "Edge calculation in progress" msgstr "Процесс вычисления границы" #: f.area.cc:2070 msgid "Area Edge Calc" msgstr "Расчет границы области" #: f.area.cc:2471 msgid "load area from a file" msgstr "" #: f.area.cc:2554 msgid "save area as a PNG file" msgstr "" #: f.area.cc:2582 msgid "position with mouse click/drag" msgstr "Укажите мышью новое положение" #: f.area.cc:2638 msgid "Paste Image" msgstr "Вставить изображение" #: f.area.cc:2643 f.batch.cc:1169 f.batch.cc:1660 msgid "resize" msgstr "Изменить размер" #: f.batch.cc:107 f.widgets.cc:531 msgid "Batch Convert" msgstr "Групповое преобразование" #: f.batch.cc:114 f.file.cc:942 msgid "New Name" msgstr "Новое Имя" #: f.batch.cc:118 msgid "Sequence Numbers" msgstr "" #: f.batch.cc:120 msgid "base" msgstr "базовое имя" #: f.batch.cc:122 msgid "adder" msgstr "модификатор" #: f.batch.cc:127 msgid "New Location" msgstr "Новое Расположение" #: f.batch.cc:132 msgid "New File Type" msgstr "Новый тип файла" #: f.batch.cc:136 f.batch.cc:144 f.repair.cc:3861 msgid "no change" msgstr "без изменения" #: f.batch.cc:139 msgid "max. Width" msgstr "макс. ширина" #: f.batch.cc:147 f.batch.cc:565 msgid "Delete Originals" msgstr "Удалять оригиналы" #: f.batch.cc:148 f.batch.cc:562 msgid "Copy Metadata" msgstr "Копировать метаданные" #: f.batch.cc:149 f.batch.cc:563 f.edit.cc:1413 f.widgets.cc:438 #: f.widgets.cc:684 msgid "Upright" msgstr "Автоповорот" #: f.batch.cc:152 f.batch.cc:564 f.batch.cc:1178 f.batch.cc:1669 #: f.repair.cc:118 f.widgets.cc:454 msgid "Sharpen" msgstr "Увеличить резкость" #: f.batch.cc:154 f.batch.cc:1180 f.batch.cc:1671 msgid "amount" msgstr "сила" #: f.batch.cc:157 f.batch.cc:1183 f.batch.cc:1674 msgid "threshold" msgstr "порог" #: f.batch.cc:280 msgid "file type not supported" msgstr "формат файла не поддерживается" #: f.batch.cc:344 msgid "cannot create new file" msgstr "невозможно создать новый файл" #: f.batch.cc:391 msgid "updating albums ..." msgstr "обновление альбомов" #: f.batch.cc:464 f.batch.cc:1456 f.batch.cc:1862 f.file.cc:1208 msgid "Select directory" msgstr "Выбрать каталог" #: f.batch.cc:513 msgid "" "new name/base/adder unreasonable\n" " e.g. newname ### 100 10\n" " or ... [oldname] ..." msgstr "" #: f.batch.cc:537 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "Макс.размер %d x %d не приемлем" #: f.batch.cc:556 #, c-format msgid "Convert %d image files" msgstr "Конвертируется %d изображений" #: f.batch.cc:557 msgid "Rename to" msgstr "Переименовать в" #: f.batch.cc:558 msgid "Convert to" msgstr "Преобразовать в" #: f.batch.cc:559 msgid "Resize within" msgstr "Новый размер" #: f.batch.cc:560 msgid "Output to" msgstr "Записать в" #: f.batch.cc:566 msgid "PROCEED?" msgstr "ПРОДОЛЖИТЬ?" #: f.batch.cc:720 f.widgets.cc:532 msgid "Batch Upright" msgstr "Пакетное ориентирование" #: f.batch.cc:726 msgid "Survey all files" msgstr "" #: f.batch.cc:780 msgid "file cannot be read" msgstr "" #: f.batch.cc:898 msgid "cannot select both options" msgstr "" #: f.batch.cc:942 f.widgets.cc:533 msgid "Batch Delete/Trash" msgstr "" #: f.batch.cc:947 msgid "delete" msgstr "" #: f.batch.cc:950 msgid "trash" msgstr "" #: f.batch.cc:1112 msgid "Batch Convert RAW Files (DCraw)" msgstr "Пакетное преобразование RAW (DCraw)" #: f.batch.cc:1121 msgid "DCraw not installed" msgstr "DCraw не установлен" #: f.batch.cc:1156 f.batch.cc:1647 msgid "output location" msgstr "выходная папка" #: f.batch.cc:1161 f.batch.cc:1652 msgid "output file type" msgstr "тип выходного файла" #: f.batch.cc:1192 msgid "white balance" msgstr "баланс белого" #: f.batch.cc:1193 msgid "interpolation" msgstr "интерполяция" #: f.batch.cc:1194 msgid "color space" msgstr "цветовое пространство" #: f.batch.cc:1195 msgid "gamma curve" msgstr "гамма-кривая" #: f.batch.cc:1198 msgid "camera" msgstr "камера" #: f.batch.cc:1199 msgid "fixed" msgstr "фиксированный" #: f.batch.cc:1200 msgid "calculated" msgstr "вычисленный" #: f.batch.cc:1217 #, fuzzy msgid "default" msgstr "по умолчанию" #: f.batch.cc:1223 msgid "defaults" msgstr "по умолчанию" #: f.batch.cc:1610 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Пакетное преобразование RAW (RAW Therapee)" #: f.batch.cc:1619 f.file.cc:428 msgid "Raw Therapee not installed" msgstr "Raw Therapee не установлен" #: f.batch.cc:1959 msgid "start" msgstr "" #: f.batch.cc:1960 msgid "begin making a script file" msgstr "" #: f.batch.cc:1962 msgid "close" msgstr "" #: f.batch.cc:1963 msgid "finish making a script file" msgstr "" #: f.batch.cc:1965 msgid "run" msgstr "" #: f.batch.cc:1966 msgid "execute a script file" msgstr "" #: f.batch.cc:2020 msgid "script already started" msgstr "" #: f.batch.cc:2024 msgid "open new script file" msgstr "" #: f.batch.cc:2056 msgid "script file error" msgstr "" #: f.batch.cc:2061 #, c-format msgid "%s added to script" msgstr "" #: f.batch.cc:2074 msgid "no script file was started" msgstr "" #: f.batch.cc:2082 msgid "script file closed" msgstr "" #: f.batch.cc:2101 msgid "script file is not closed" msgstr "" #: f.batch.cc:2105 msgid "select script file to run" msgstr "" #: f.batch.cc:2107 msgid "open script file" msgstr "" #: f.batch.cc:2114 msgid "unknown script file" msgstr "" #: f.batch.cc:2121 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" #: f.batch.cc:2132 msgid "select image files to be processed" msgstr "" #: f.batch.cc:2144 f.batch.cc:2193 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" #: f.batch.cc:2165 #, c-format msgid "unknown edit function: %s" msgstr "" #: f.batch.cc:2174 #, c-format msgid "load widgets failed: %s" msgstr "" #: f.batch.cc:2203 #, c-format msgid "script file format error: %s" msgstr "" #: f.batch.cc:2229 msgid "Brasero not installed" msgstr "Brasero не установлен" #: f.batch.cc:2318 f.widgets.cc:538 msgid "Find Duplicate Images" msgstr "Поиск повторяющихся изображений" #: f.batch.cc:2320 f.tools.cc:929 msgid "Thumbnail size" msgstr "Размер иконки" #: f.batch.cc:2323 msgid "pixel difference" msgstr "попиксельная разница" #: f.batch.cc:2326 msgid "pixel count" msgstr "количество пикселей" #: f.batch.cc:2329 msgid "Images:" msgstr "Изображений:" #: f.batch.cc:2330 msgid "searching ..." msgstr "поиск..." #: f.batch.cc:2332 msgid "Duplicates:" msgstr "Дублей:" #: f.batch.cc:2350 #, c-format msgid "only %d image thumbnails found" msgstr "найдено лишь %d иконок" #: f.bend.cc:86 f.widgets.cc:475 msgid "Unbend" msgstr "Выпрямить изображение" #: f.bend.cc:94 f.edit.cc:4267 f.effects.cc:4725 msgid "vertical" msgstr "вертикаль" #: f.bend.cc:95 f.edit.cc:4266 f.effects.cc:4721 msgid "horizontal" msgstr "горизонталь" #: f.bend.cc:96 msgid "linear" msgstr "Линейный" #: f.bend.cc:99 msgid "curved" msgstr "Изогнутый" #: f.bend.cc:367 msgid "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." msgstr "" "Кликните на четырех углах площади четырехугольника. Нажмите [Применить] \n" "Четырехугольник станет прямоугольником." #: f.bend.cc:384 msgid "Perspective Correction" msgstr "Коррекция перстпективы" #: f.bend.cc:590 msgid "must have 4 corners" msgstr "должно быть 4 угла" #: f.bend.cc:712 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 "" "Выберите область деформации (Меню -> Области -> Выбрать). \n" "Нажмите [Начать деформацию] и потяните область мышью. \n" "При необходимости сделайте несколько движений . \n" "Для завершения выберите другую область или нажмите [Изменить]" #: f.bend.cc:725 f.widgets.cc:477 msgid "Warp area" msgstr "Деформация области" #: f.bend.cc:730 msgid "start warp" msgstr "Начать деформацию" #: f.bend.cc:788 msgid "no active Select Area" msgstr "Не выбрана активная область" #: f.bend.cc:1129 f.bend.cc:1443 msgid "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" "Потяните положение изображения с помощью мыши. \n" "При необходимости сделайте несколько движений . \n" "По окончании нажмите [Изменить]." #: f.bend.cc:1147 f.widgets.cc:478 msgid "Warp curved" msgstr "Деформация в кривых" #: f.bend.cc:1156 f.mashup.cc:1836 msgid "warp span" msgstr "область искривления" #: f.bend.cc:1461 f.widgets.cc:479 msgid "Warp linear" msgstr "Деформация линейная" #: f.bend.cc:1776 msgid "" " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" "Потяните угол изображения с помощью мыши \n" "При необходимости сделайте несколько движений . \n" "По окончании нажмите [Изменить]" #: f.bend.cc:1792 f.widgets.cc:480 msgid "Warp affine" msgstr "Деформация аффинная" #: f.bend.cc:2138 msgid "Flatten Book Page Photo" msgstr "Выровнять фотографию книжной страницы" #: f.bend.cc:2139 msgid "" "Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: " msgstr "" "Выровнять выделенную кромку одной страницы. \n" "Карта верхней и нижней границ в \n" "4+ клика мыши, затем растянуть: " #: f.bend.cc:2142 msgid "Stretch curved-down surfaces:" msgstr "Растянуть изогнутые вниз поверхности:" #: f.bend.cc:2197 msgid "top:" msgstr "Верх:" #: f.bend.cc:2200 msgid "bottom:" msgstr "Низ:" #: f.combine.cc:2108 f.combine.cc:2740 f.combine.cc:3396 f.combine.cc:3928 msgid "Select 2 to 9 files" msgstr "Выберите от 2 до 9 файлов" #: f.combine.cc:2129 f.combine.cc:2761 f.combine.cc:3417 f.combine.cc:3949 msgid "Images are not all the same size" msgstr "Изображения разного размера" #: f.combine.cc:2473 msgid "Adjust Image Contributions" msgstr "Настройка состава изображения" #: f.combine.cc:2477 msgid "dark pixels" msgstr "Темные пиксели" #: f.combine.cc:2479 msgid "light pixels" msgstr "Светлые пиксели" #: f.combine.cc:2481 msgid "file:" msgstr "Файл:" #: f.combine.cc:2997 msgid "Paint and Warp Image" msgstr "Окраска и деформация изображения" #: f.combine.cc:3001 f.combine.cc:3646 f.combine.cc:6237 f.effects.cc:3912 #: f.widgets.cc:567 f.widgets.cc:590 f.widgets.cc:612 msgid "Image" msgstr "Изображение" #: f.combine.cc:3005 msgid "paint" msgstr "Окраска" #: f.combine.cc:3006 msgid "warp" msgstr "Деформация" #: f.combine.cc:3644 msgid "Select and Paint Image" msgstr "Выбор и окраска изображения" #: f.combine.cc:4120 msgid "Adjust Pixel Composition" msgstr "Настроить попиксельное объединение" #: f.combine.cc:4122 msgid "use average" msgstr "использовать среднее" #: f.combine.cc:4123 msgid "use median" msgstr "использовать медиану" #: f.combine.cc:4125 msgid "omit low pixel" msgstr "пропустить тёмный пиксель" #: f.combine.cc:4126 msgid "omit high pixel" msgstr "пропустить светлый пиксель" #: f.combine.cc:4388 f.combine.cc:5494 msgid "Select 2 to 4 files" msgstr "Выберите от 2 до 4 файлов" #: f.combine.cc:4463 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from lower edge." msgstr "" "Потяните изображение для грубого выравнивания.\n" "Для вращения потяните за нижний край." #: f.combine.cc:4465 f.combine.cc:5571 msgid "no curve (scanned image)" msgstr "кривая отсутствует (сканированное изображение)" #: f.combine.cc:4466 f.combine.cc:5572 msgid "Search for lens mm" msgstr "Поиск фокусного расстояния" #: f.combine.cc:4467 f.combine.cc:5573 msgid "Save lens mm → image EXIF" msgstr "Сохранить фокусное расстояние в EXIF" #: f.combine.cc:4528 f.combine.cc:5634 msgid "Pre-align Images" msgstr "Предварительное выравнивание изображений" #: f.combine.cc:4532 f.combine.cc:5638 msgid "lens mm" msgstr "Фокусное расстояние" #: f.combine.cc:4537 f.combine.cc:5643 msgid "no auto warp" msgstr "без автодеформации" #: f.combine.cc:4539 f.combine.cc:5645 f.widgets.cc:445 f.widgets.cc:683 msgid "Resize" msgstr "Изменить размер" #: f.combine.cc:4540 f.combine.cc:5646 msgid "resize window" msgstr "Изменить размер окна" #: f.combine.cc:4548 f.combine.cc:5654 msgid "do not warp images during auto-alignment" msgstr "не деформировать изображение при автовыравнивании" #: f.combine.cc:4593 f.combine.cc:5699 msgid "use two images only" msgstr "Использовать только два изображения" #: f.combine.cc:4621 f.combine.cc:4825 f.combine.cc:5011 f.combine.cc:5725 #: f.combine.cc:5929 f.combine.cc:6115 msgid "Too little overlap, cannot align" msgstr "Слишком маленькое наложение, не удается выровнять" #: f.combine.cc:5089 f.combine.cc:6193 msgid "Match Brightness and Color" msgstr "Подбор яркости и цветности" #: f.combine.cc:5134 msgid "Select image" msgstr "Выберите изображение" #: f.combine.cc:5149 f.combine.cc:6252 msgid "auto color" msgstr "Автокоррекция цветов" #: f.combine.cc:5150 f.combine.cc:6253 msgid "file color" msgstr "Сохранить цвета" #: f.combine.cc:5156 f.combine.cc:6259 msgid "mouse warp" msgstr "деформация мышью" #: f.combine.cc:5159 msgid "curved image" msgstr "убрать искажения" #: f.combine.cc:5569 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from right edge." msgstr "" "Потяните изображение для грубого выравнивания.\n" "Для вращения потяните за правый край." #: f.combine.cc:6469 msgid "pano tools (hugin) not installed" msgstr "" #: f.combine.cc:6478 msgid "Select at least 2 files" msgstr "Выберите не менее 2 файлов" #: f.edit.cc:98 msgid "Trim: drag middle to move, drag corners to resize" msgstr "Обрезка: перемещение за середину, изменение размера за углы" #: f.edit.cc:99 msgid "Minor rotate: drag right edge with mouse" msgstr "Плавное вращение перетаскивание за правый край" #: f.edit.cc:173 f.widgets.cc:437 f.widgets.cc:682 msgid "Trim/Rotate" msgstr "Обрезать/Повернуть" #: f.edit.cc:185 f.edit.cc:586 msgid "ratio" msgstr "Соотношение" #: f.edit.cc:189 msgid "trim size:" msgstr "размер обрезки:" #: f.edit.cc:194 msgid "Lock Ratio" msgstr "Фиксированное соотношение" #: f.edit.cc:199 msgid "Customize" msgstr "Настройка" #: f.edit.cc:207 msgid "Rotate: degrees" msgstr "Поворот: градусы" #: f.edit.cc:209 msgid "auto-trim" msgstr "автообрезка" #: f.edit.cc:582 msgid "Trim Buttons" msgstr "Кнопки обрезки" #: f.edit.cc:585 msgid "label" msgstr "Метка" #: f.edit.cc:1411 msgid "Upright Image" msgstr "Вертикальное изображение" #: f.edit.cc:1477 msgid "rotation unknown" msgstr "ориентация неизвестна" #: f.edit.cc:2029 f.widgets.cc:441 f.widgets.cc:686 msgid "Retouch Combo" msgstr "Ретушь" #: f.edit.cc:2050 msgid "Amplifier" msgstr "Усилитель" #: f.edit.cc:2051 fotoxx.h:1091 msgid "Brightness" msgstr "Яркость" #: f.edit.cc:2052 f.effects.cc:3364 fotoxx.h:1099 msgid "Contrast" msgstr "Контраст" #: f.edit.cc:2053 msgid "Low Color" msgstr "Цвет мин" #: f.edit.cc:2054 msgid "Warmer" msgstr "Теплее" #: f.edit.cc:2055 f.effects.cc:1011 msgid "Dark Areas" msgstr "Тёмные зоны" #: f.edit.cc:2064 msgid "Max." msgstr "Макс." #: f.edit.cc:2065 f.edit.cc:2066 f.edit.cc:2067 msgid "High" msgstr "Макс" #: f.edit.cc:2068 msgid "Cooler" msgstr "Холоднее" #: f.edit.cc:2069 msgid "Bright" msgstr "Светлые" #: f.edit.cc:2072 f.tools.cc:1631 msgid "Brightness Distribution" msgstr "Распределение яркости" #: f.edit.cc:2075 msgid "Click for white balance or black level" msgstr "Устанавливать ББ и точку чёрного" #: f.edit.cc:2078 msgid "Settings File" msgstr "Файл установок" #: f.edit.cc:2082 msgid "recall previous settings used" msgstr "вернут предыдущие установки" #: f.edit.cc:2726 msgid "Adjust Brightness Distribution" msgstr "Выровнять распределение яркости" #: f.edit.cc:2765 msgid "Low Cutoff" msgstr "" #: f.edit.cc:2766 msgid "High Cutoff" msgstr "" #: f.edit.cc:2767 msgid "Low Flatten" msgstr "" #: f.edit.cc:2768 msgid "Mid Flatten" msgstr "" #: f.edit.cc:2769 msgid "High Flatten" msgstr "" #: f.edit.cc:2770 msgid "Low Stretch" msgstr "" #: f.edit.cc:2771 msgid "Mid Stretch" msgstr "" #: f.edit.cc:2772 msgid "High Stretch" msgstr "" #: f.edit.cc:3102 msgid "Zonal Flatten Brightness" msgstr "" #: f.edit.cc:3135 msgid "Zones" msgstr "" #: f.edit.cc:3142 msgid "Deband Dark" msgstr "" #: f.edit.cc:3145 msgid "Deband Bright" msgstr "" #: f.edit.cc:3594 f.widgets.cc:444 f.widgets.cc:689 msgid "Tone Mapping" msgstr "Преобразование тона" #: f.edit.cc:3625 msgid "low" msgstr "Низкий" #: f.edit.cc:3627 msgid "high" msgstr "Высокий" #: f.edit.cc:3630 msgid "Amplify" msgstr "Усилить" #: f.edit.cc:4034 msgid "Resize Image" msgstr "Изменить размер изображения" #: f.edit.cc:4059 msgid "W/H Ratio:" msgstr "" #: f.edit.cc:4061 msgid "Lock" msgstr "" #: f.edit.cc:4063 msgid "use previous settings" msgstr "" #: f.edit.cc:4262 f.widgets.cc:446 msgid "Flip" msgstr "Отразить зеркально" #: f.edit.cc:4368 msgid "+Version" msgstr "" #: f.edit.cc:4391 msgid "Write Text on Image" msgstr "Написать текст на изображении" #: f.edit.cc:4392 msgid "Enter text, click/drag on image, right click to remove" msgstr "" "Введите текст, кликните/переместите по изображению, клик правой кнопкой для " "удаления" #: f.edit.cc:4441 f.mashup.cc:2437 msgid "Text" msgstr "Текст" #: f.edit.cc:4446 msgid "Use metadata key" msgstr "" #: f.edit.cc:4451 msgid "Use text file" msgstr "" #: f.edit.cc:4470 f.mashup.cc:2455 msgid "text" msgstr "Текст" #: f.edit.cc:4471 f.edit.cc:5309 f.mashup.cc:2456 f.mashup.cc:2798 msgid "backing" msgstr "Фон" #: f.edit.cc:4472 f.edit.cc:5310 f.mashup.cc:2457 f.mashup.cc:2799 msgid "outline" msgstr "Контур" #: f.edit.cc:4473 f.edit.cc:5311 f.mashup.cc:2458 f.mashup.cc:2800 msgid "shadow" msgstr "тень" #: f.edit.cc:4499 msgid "save to current file" msgstr "" #: f.edit.cc:4500 f.file.cc:1909 msgid "save as new file version" msgstr "сохранить как новую версию" #: f.edit.cc:4501 msgid "" "save to current file \n" "open next file with same text" msgstr "" #: f.edit.cc:4662 f.mashup.cc:2557 f.tools.cc:1068 msgid "select font" msgstr "Выбрать шрифт" #: f.edit.cc:4926 msgid "text file is defective" msgstr "текстовый файл повреждён" #: f.edit.cc:5249 msgid "" "Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove" msgstr "" "Введите свойства линии/стрелки в диалог \n" "клик/перемещение по полю, правый клик для удаления" #: f.edit.cc:5280 msgid "Write Line or Arrow on Image" msgstr "Добавить линию/стрелку на изображение" #: f.edit.cc:5288 f.mashup.cc:2777 msgid "Line length" msgstr "Длина линии" #: f.edit.cc:5295 f.mashup.cc:2784 msgid "Arrow head" msgstr "Направление стрелки" #: f.edit.cc:5308 f.mashup.cc:2797 msgid "line" msgstr "линия" #: f.edit.cc:5337 msgid "" "fix line/arrow in layout \n" " start new line/arrow" msgstr "" "фиксировать линию/стрелку в размещении \n" " начать новую линию/стрелку" #: f.edit.cc:5926 f.widgets.cc:449 msgid "Paint Edits" msgstr "Редактор окраски" #: f.edit.cc:5933 f.edit.cc:6162 fotoxx-15.11.cc:3179 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "Избранная область не может быть сохранена.\n" "Продолжить?" #: f.edit.cc:5941 f.edit.cc:6170 f.tools.cc:2157 msgid "Edit function must be active" msgstr "Функция редактирования должна быть активна" #: f.edit.cc:5946 msgid "Cannot use Paint Edits" msgstr "" #: f.edit.cc:5975 msgid "power: center" msgstr "Усиление: центр" #: f.edit.cc:5980 msgid "reset area" msgstr "Сбросить область" #: f.edit.cc:6155 f.widgets.cc:450 msgid "Leverage Edits" msgstr "Нелинейное редактирование" #: f.edit.cc:6156 msgid "Edit Function Amplifier" msgstr "Усилитель функций редактирования" #: f.edit.cc:6175 msgid "Cannot use Leverage Edits" msgstr "" #: f.edit.cc:6208 msgid "minimum" msgstr "минимум" #: f.edit.cc:6210 msgid "maximum" msgstr "максимум" #: f.edit.cc:6510 msgid "Edit Plugins" msgstr "Редактировать плагины" #: f.edit.cc:6511 msgid "Edit plugins menu" msgstr "Редактировать меню плагинов" #: f.edit.cc:6519 msgid "Run as Fotoxx edit function" msgstr "Запустить как функцию редактирования Fotoxx" #: f.edit.cc:6757 msgid "Plugin working ..." msgstr "Работает плагин..." #: f.edit.cc:6766 msgid "plugin failed" msgstr "сбой плагина" #: f.effects.cc:64 msgid "Set color depth to 1-16 bits" msgstr "Установите глубину цвета в диапазоне от 1 до 16 бит " #: f.effects.cc:76 msgid "Set Color Depth" msgstr "Установка глубины цвета" #: f.effects.cc:220 msgid "Convert to Sketch" msgstr "Преобразовать в Абрис" #: f.effects.cc:296 msgid "Clip Level" msgstr "Уровень усечения" #: f.effects.cc:300 msgid "Algorithm" msgstr "Алгоритм" #: f.effects.cc:305 msgid "Foreground" msgstr "" #: f.effects.cc:308 msgid "Background" msgstr "" #: f.effects.cc:714 f.widgets.cc:486 msgid "Line Drawing" msgstr "Рисунок линиями" #: f.effects.cc:727 msgid "black/white" msgstr "ч.б" #: f.effects.cc:1005 f.widgets.cc:487 msgid "Color Drawing" msgstr "Рисунок цветом" #: f.effects.cc:1012 msgid "Bright Areas" msgstr "Яркие области" #: f.effects.cc:1266 f.widgets.cc:488 msgid "Graduated Blur" msgstr "Размытие полутонов" #: f.effects.cc:1270 msgid "Contrast Limit" msgstr "Ограничение контрастности" #: f.effects.cc:1273 f.repair.cc:775 msgid "Blur Radius" msgstr "Радиус размытия" #: f.effects.cc:1487 msgid "Simulate Embossing" msgstr "Имитировать чеканку" #: f.effects.cc:1504 msgid "depth" msgstr "Глубина" #: f.effects.cc:1706 msgid "Simulate Tiles" msgstr "Имитировать мозаику" #: f.effects.cc:1710 msgid "tile size" msgstr "Размер мозаики" #: f.effects.cc:1713 msgid "tile gap" msgstr "Расстояние между элементами" #: f.effects.cc:1716 msgid "3D depth" msgstr "Глубина 3D" #: f.effects.cc:1940 msgid "Convert Image to Dots" msgstr "Преобразование изображения в точки" #: f.effects.cc:1944 msgid "dot size" msgstr "Размер точки" #: f.effects.cc:2169 msgid "Simulate Painting" msgstr "Имитировать живопись" #: f.effects.cc:2173 msgid "color depth" msgstr "Глубина цвета" #: f.effects.cc:2177 msgid "patch area goal" msgstr "Мозаичные области" #: f.effects.cc:2181 msgid "req. color match" msgstr "Точность совпадения цвета " #: f.effects.cc:2185 msgid "borders" msgstr "Кромки" #: f.effects.cc:2758 f.widgets.cc:493 msgid "Vignette" msgstr "Инструмент виньетка" #: f.effects.cc:3107 msgid "Add Texture" msgstr "Добавить текстуру" #: f.effects.cc:3324 msgid "Background Pattern" msgstr "Паттерн фона" #: f.effects.cc:3328 msgid "Pattern File:" msgstr "Файл паттерна:" #: f.effects.cc:3333 msgid "Geometry" msgstr "Геометрия" #: f.effects.cc:3334 msgid "Calculate" msgstr "Вычислить" #: f.effects.cc:3336 msgid "Zoom" msgstr "Приблизить" #: f.effects.cc:3347 f.widgets.cc:495 msgid "Pattern" msgstr "Паттерн" #: f.effects.cc:3354 msgid "Overlap" msgstr "Наложение" #: f.effects.cc:3361 msgid "Opacity" msgstr "Прозрачность" #: f.effects.cc:3429 msgid "choose pattern tile" msgstr "выбрать элемент паттерна" #: f.effects.cc:3816 msgid "Create Mosaic" msgstr "Создать мозаику" #: f.effects.cc:3902 msgid "Tile" msgstr "Плитка" #: f.effects.cc:3910 f.widgets.cc:490 msgid "Tiles" msgstr "Мозаика" #: f.effects.cc:3916 msgid "Tile blending" msgstr "Смешивание плитки" #: f.effects.cc:3996 #, c-format msgid "exceeded max. tiles: %d" msgstr "превышен максимум в %d плиток" #: f.effects.cc:4011 #, c-format msgid "only %d tile images found" msgstr "найдено только %d файлов плиток" #: f.effects.cc:4404 f.widgets.cc:497 msgid "Custom Kernel" msgstr "Матричное преобразование" #: f.effects.cc:4408 msgid "Kernel size" msgstr "Размер ядра" #: f.effects.cc:4411 msgid "Divisor" msgstr "Делитель" #: f.effects.cc:4414 msgid "Data file" msgstr "Файл данных" #: f.effects.cc:4450 fotoxx-15.11.cc:3440 msgid "Load settings from file" msgstr "Загрузка установок из файлв" #: f.effects.cc:4711 f.widgets.cc:498 msgid "Make Waves" msgstr "Волны" #: f.effects.cc:4718 msgid "wavelength" msgstr "длина волны" #: f.effects.cc:4719 msgid "amplitude" msgstr "амплитуда" #: f.effects.cc:4720 msgid "variance" msgstr "отклонение" #: f.effects.cc:4731 msgid "perspective" msgstr "перспектива" #: f.effects.cc:4905 msgid "Pull image using the mouse." msgstr "" #: f.effects.cc:4926 f.widgets.cc:499 msgid "Directed Blur" msgstr "" #: f.effects.cc:4930 msgid "blur span" msgstr "" #: f.effects.cc:4933 msgid "intensity" msgstr "" #: f.effects.cc:5099 f.widgets.cc:500 msgid "Spherical Projection" msgstr "" #: f.effects.cc:5143 msgid "Magnify" msgstr "" #: f.file.cc:186 msgid "" "use EXIF photo date or \n" " file modification date" msgstr "" #: f.file.cc:203 f.widgets.cc:572 msgid "File" msgstr "Файл" #: f.file.cc:357 msgid "UFraw not installed" msgstr "UFraw не установлен" #: f.file.cc:373 f.widgets.cc:691 msgid "Open RAW file (ufraw)" msgstr "Открыть RAW файл (ufraw)" #: f.file.cc:378 f.file.cc:449 msgid "RAW type not registered in User Settings" msgstr "Данный тип RAW отсутствует в настройках пользователя" #: f.file.cc:444 f.widgets.cc:692 msgid "Open RAW file (Raw Therapee)" msgstr "Открыть файл RAW (Raw Therapee)" #: f.file.cc:541 f.widgets.cc:395 msgid "Open Image File" msgstr "Открыть файл изображения" #: f.file.cc:560 msgid "unknown file type" msgstr "неизвестный тип файла" #: f.file.cc:753 msgid "Create Blank Image" msgstr "Создать пустое изображение" #: f.file.cc:755 msgid "file name" msgstr "Имя файла" #: f.file.cc:788 msgid "supply a file name" msgstr "введите имя файлв" #: f.file.cc:934 f.widgets.cc:400 msgid "Rename Image File" msgstr "Переименовать файл изображения" #: f.file.cc:941 msgid "Old Name" msgstr "Старое Имя" #: f.file.cc:951 msgid "previous name" msgstr "предыдущее имя" #: f.file.cc:952 msgid "add 1" msgstr "Плюс 1" #: f.file.cc:1120 msgid "Copy Image File" msgstr "Копировать изображение" #: f.file.cc:1121 msgid "Move Image File" msgstr "Переместить изображение" #: f.file.cc:1164 msgid "new location" msgstr "Новое расположение" #: f.file.cc:1236 msgid "new location is not a directory" msgstr "Местоположение каталога не действительно" #: f.file.cc:1276 f.file.cc:1518 f.file.cc:1656 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "Удаление не удалось: \n" " %s" #: f.file.cc:1363 f.widgets.cc:401 msgid "Trash Image File" msgstr "Поместить изображение в корзину" #: f.file.cc:1364 f.file.cc:1571 msgid "(automatic step to next image)" msgstr "(автоматический переход к следующему изображению)" #: f.file.cc:1432 msgid "" "Linux standard trash does not work. \n" "Desktop trash folder will be created." msgstr "" "Стандартная корзина Linux не работает \n" "Корзина будет создана на рабочем столе." #: f.file.cc:1435 msgid "" "Linux and Desktop trash do not work. \n" "Permanently delete the image file?" msgstr "" "Корзина для Linux не работает. \n" "Удалить файл окончательно?" #: f.file.cc:1438 #, c-format msgid "Cannot create trash folder: %s" msgstr "Не удалось создать папку корзины: %s" #: f.file.cc:1450 msgid "Move read-only file to trash?" msgstr "Переместить файл только для чтения в корзину?" #: f.file.cc:1505 #, c-format msgid "error: %s" msgstr "Ошибка: %s" #: f.file.cc:1534 f.file.cc:1673 f.file.cc:2508 msgid "no more images" msgstr "последнее изображение" #: f.file.cc:1570 msgid "Delete Image File - CANNOT BE REVERSED" msgstr "Удаление изображения (НЕОБРАТИМО!)" #: f.file.cc:1646 msgid "Delete read-only file?" msgstr "Удалить файл только для чтения?" #: f.file.cc:1891 msgid "Save Image File" msgstr "Сохранение Файла Изображения" #: f.file.cc:1901 msgid "new version" msgstr "новая версия" #: f.file.cc:1902 msgid "new file" msgstr "новый файл" #: f.file.cc:1903 msgid "replace file" msgstr "замещение файла" #: f.file.cc:1910 f.file.cc:2281 msgid "save as new file name or type" msgstr "сохранить с новым именем/форматом" #: f.file.cc:1912 msgid "replace old file (OVERWRITE)" msgstr "замещение файла (ПЕРЕЗАПИСЬ)" #: f.file.cc:1950 f.file.cc:2057 msgid "cannot save as RAW type" msgstr "невозможно сохранить в RAW" #: f.file.cc:2051 msgid "" "Transparency map will be lost.\n" "save to PNG file to retain." msgstr "" #: f.file.cc:2191 msgid "Unable to copy EXIF/IPTC data" msgstr "Невозможно скопировать данные EXIF/IPTC" #: f.file.cc:2299 f.file.cc:2302 msgid "make current" msgstr "Сделать текущим" #: f.file.cc:2300 msgid "(new file becomes current file)" msgstr "" #: f.file.cc:2409 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Перезаписать файл? \n" " %s" #: f.file.cc:2555 f.widgets.cc:554 msgid "Quick Start" msgstr "Быстрый Старт" #: f.file.cc:2558 f.widgets.cc:555 msgid "User Guide" msgstr "Руководство пользователя" #: f.file.cc:2561 f.widgets.cc:556 msgid "User Guide Changes" msgstr "Изменения в руководстве пользователя" #: f.file.cc:2564 f.widgets.cc:558 msgid "README" msgstr "README" #: f.file.cc:2567 f.widgets.cc:557 msgid "Edit Functions Summary" msgstr "Краткое изложение функций" #: f.file.cc:2570 f.widgets.cc:559 msgid "Change Log" msgstr "Журнал изменений" #: f.file.cc:2573 f.widgets.cc:560 msgid "Log File" msgstr "Файл журнала" #: f.file.cc:2576 f.widgets.cc:561 msgid "Translations" msgstr "Переводы" #: f.file.cc:2579 f.widgets.cc:562 msgid "Home Page" msgstr "Домашняя страница" #: f.file.cc:2582 f.widgets.cc:563 msgid "About" msgstr "О программе" #: f.file.cc:2586 f.widgets.cc:584 f.widgets.cc:606 fotoxx.h:1123 msgid "Help" msgstr "Справка" #: f.file.cc:2739 f.file.cc:2808 #, c-format msgid "file not found: %s" msgstr "Файл не найден: %s" #: f.file.cc:2745 f.file.cc:2814 #, c-format msgid "file type not supported: %s" msgstr "тип файла не поддерживается: %s" #: f.gallery.cc:918 f.gallery.cc:1007 f.widgets.cc:604 msgid "Scroll" msgstr "Скролл" #: f.gallery.cc:922 msgid "Sync" msgstr "Синхронизация" #: f.gallery.cc:932 f.repair.cc:5845 f.repair.cc:5850 fotoxx.h:1144 msgid "Open" msgstr "Открыть" #: f.gallery.cc:933 msgid "change directory" msgstr "Смена каталога" #: f.gallery.cc:941 msgid "GoTo" msgstr "Переход" #: f.gallery.cc:947 f.widgets.cc:599 msgid "Sort" msgstr "Сортировка" #: f.gallery.cc:953 f.gallery.cc:1517 f.gallery.cc:1518 f.gallery.cc:1520 #: f.widgets.cc:597 msgid "Zoom+" msgstr "Увеличить" #: f.gallery.cc:963 f.gallery.cc:1519 f.gallery.cc:1521 f.widgets.cc:598 msgid "Zoom-" msgstr "Уменьшить" #: f.gallery.cc:975 f.gallery.cc:1525 msgid "Row↑" msgstr "Строка↑" #: f.gallery.cc:983 f.gallery.cc:1526 msgid "Row↓" msgstr "Строка↓" #: f.gallery.cc:991 f.gallery.cc:1523 f.gallery.cc:1530 f.widgets.cc:602 msgid "Page↑" msgstr "Страница↑" #: f.gallery.cc:999 f.gallery.cc:1524 f.gallery.cc:1531 f.widgets.cc:603 msgid "Page↓" msgstr "Страница↓" #: f.gallery.cc:1013 f.gallery.cc:1528 f.widgets.cc:600 msgid "First" msgstr "В начало" #: f.gallery.cc:1014 f.gallery.cc:1529 f.widgets.cc:601 msgid "Last" msgstr "В конец" #: f.gallery.cc:1127 msgid "Choose image directory" msgstr "Выберите папку для файлов" #: f.gallery.cc:1134 f.gallery.cc:1148 msgid "recent" msgstr "недавние" #: f.gallery.cc:1135 f.gallery.cc:1153 msgid "newest" msgstr "новые" #: f.gallery.cc:1217 msgid "no albums found" msgstr "альбомы не найдены" #: f.gallery.cc:1224 msgid "Choose album" msgstr "Выберите альбом" #: f.gallery.cc:1274 msgid "Gallery Sort" msgstr "Сортировка галереи" #: f.gallery.cc:1278 msgid "File Name" msgstr "Имя файла" #: f.gallery.cc:1279 msgid "File Mod Date/Time" msgstr "Время/Дата модификации" #: f.gallery.cc:1280 msgid "Photo Date/Time (EXIF)" msgstr "Дата/время изображения (EXIF)" #: f.gallery.cc:1282 msgid "ascending" msgstr "по нарастанию" #: f.gallery.cc:1283 msgid "descending" msgstr "по убыванию" #: f.gallery.cc:2212 fotoxx.h:1169 msgid "Select Files" msgstr "Выбрать файлы" #: f.gallery.cc:2672 msgid "Click list position. Click thumbnail to add." msgstr "Щёлкните по элементу списка. Щелкните по миниатюре для добавления" #: f.gallery.cc:2698 f.gallery.cc:2946 msgid "Edit Bookmarks" msgstr "Редактировать закладки" #: f.gallery.cc:2873 msgid "unable to save bookmarks file" msgstr "невозможно сокранить файл закладок" #: f.gallery.cc:2946 msgid "Go To Bookmark" msgstr "Перейти по закладке" #: f.gallery.cc:3003 f.meta.cc:2844 f.repair.cc:7473 msgid "file not found" msgstr "Файл не найден" #: f.mashup.cc:170 msgid "Mashup layout and background image" msgstr "Макет и фоновое изображение Коллажа" #: f.mashup.cc:213 msgid "choose an image file" msgstr "выберите файл изображения" #: f.mashup.cc:214 msgid "use current image file" msgstr "использование текущего файла изображения" #: f.mashup.cc:215 msgid "specify layout size and color" msgstr "задайте размер и цвет фона макета" #: f.mashup.cc:216 msgid "open a Mashup project file" msgstr "открыть файл проекта Коллажа" #: f.mashup.cc:241 msgid "choose layout file" msgstr "выберите файл макета" #: f.mashup.cc:259 msgid "no current file" msgstr "нет текущего файла" #: f.mashup.cc:291 msgid "Make Layout Image" msgstr "Создать Макет" #: f.mashup.cc:293 msgid "project name" msgstr "имя проекта" #: f.mashup.cc:319 msgid "supply a project name" msgstr "введите имя проекта" #: f.mashup.cc:417 msgid "Edit Images" msgstr "Редактирование Изображений" #: f.mashup.cc:418 f.mashup.cc:2432 msgid "Edit Text" msgstr "Редактирование текста" #: f.mashup.cc:419 msgid "Edit Line" msgstr "Редактировать Линию" #: f.mashup.cc:420 msgid "Rescale" msgstr "Масштабирование" #: f.mashup.cc:423 msgid "add or edit images" msgstr "добавить или редактировать изображения" #: f.mashup.cc:424 msgid "add or edit text" msgstr "добавить или редактировать текст" #: f.mashup.cc:425 msgid "add or edit lines/arrows" msgstr "добавить/редактировать линии/стрелки" #: f.mashup.cc:426 msgid "change project scale" msgstr "изменение масштаба проекта" #: f.mashup.cc:427 msgid "project complete" msgstr "проект завершён" #: f.mashup.cc:428 msgid "cancel project" msgstr "отменить проект" #: f.mashup.cc:446 msgid "rescale project" msgstr "масштабирование проекта" #: f.mashup.cc:501 msgid "save Mashup output file" msgstr "сохранить файл" #: f.mashup.cc:512 msgid "save Mashup project file?" msgstr "" #: f.mashup.cc:524 msgid "delete Mashup project file?" msgstr "" #: f.mashup.cc:583 msgid "Open Project" msgstr "Открыть проект" #: f.mashup.cc:612 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "файл макета не найден: \n" " %s" #: f.mashup.cc:669 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "файл оверлея не найден: \n" " %s" #: f.mashup.cc:974 msgid "project file is defective" msgstr "файл проекта повреждён" #: f.mashup.cc:1004 msgid "Save Project" msgstr "Сохранить проект" #: f.mashup.cc:1145 msgid "layout exceeds 2 gigabytes" msgstr "размер макета превысил 2ГБ" #: f.mashup.cc:1219 msgid "Click image to select, drag image to move." msgstr "Клик для выбора изображения, размещение мышкой." #: f.mashup.cc:1220 msgid "Make black margins transparent" msgstr "Сделать чёрные границы прозрачными" #: f.mashup.cc:1259 msgid "Current image:" msgstr "Текущее изображение:" #: f.mashup.cc:1263 msgid "Cycle through images:" msgstr "Циклический перебор изображений" #: f.mashup.cc:1270 msgid "Scale" msgstr "Масштаб" #: f.mashup.cc:1279 msgid "Stacking Order" msgstr "Порядок наложения" #: f.mashup.cc:1280 msgid "raise" msgstr "поднять" #: f.mashup.cc:1281 msgid "lower" msgstr "опустить" #: f.mashup.cc:1284 msgid "Base Transparency" msgstr "Базовая Прозрачность" #: f.mashup.cc:1288 msgid "Var. Transparency" msgstr "Перем. Прозрачность" #: f.mashup.cc:1289 msgid "Paint" msgstr "Рисовать" #: f.mashup.cc:1292 msgid "Bend and fine-align" msgstr "Сгиб и преобразования" #: f.mashup.cc:1293 msgid "Warp" msgstr "Деформация" #: f.mashup.cc:1302 msgid "Margins" msgstr "Границы" #: f.mashup.cc:1303 msgid "Hard" msgstr "Резкая" #: f.mashup.cc:1304 f.repair.cc:5441 msgid "Blend" msgstr "Смешивание" #: f.mashup.cc:1318 msgid "add images to layout" msgstr "добавить изображения к размещению" #: f.mashup.cc:1557 msgid "Paint Image Transparencies" msgstr "Изменение Прозрачности Мышью" #: f.mashup.cc:1575 msgid "Gradual" msgstr "Постепенный" #: f.mashup.cc:1580 fotoxx.h:1148 msgid "Power" msgstr "Скорость" #: f.mashup.cc:1814 msgid "Pull on the image with the mouse." msgstr "Перетащите изображение мышью" #: f.mashup.cc:1830 msgid "Warp Image" msgstr "Деформация изображения" #: f.mashup.cc:2263 #, c-format msgid "exceeded %d images" msgstr "превышено %d изображений" #: f.mashup.cc:2405 msgid "Enter text, [Add] to layout, edit properties." msgstr "" #: f.mashup.cc:2485 msgid "Text File:" msgstr "Текстовый файл:" #: f.mashup.cc:2489 msgid "add entered text to layout" msgstr "добавить текст к размещению" #: f.mashup.cc:2618 msgid "click position to add text" msgstr "" #: f.mashup.cc:2624 #, c-format msgid "exceeded %d text entries" msgstr "превышено %d блоков текста" #: f.mashup.cc:2747 msgid "Set line properties, [Add] to layout, edit." msgstr "" #: f.mashup.cc:2771 msgid "Edit Line/Arrow" msgstr "Ввести Линию/Стрелку" #: f.mashup.cc:2826 msgid "add line/arrow to layout" msgstr "добавить линию/стрелку к размещению" #: f.mashup.cc:2919 msgid "click position to add line" msgstr "" #: f.mashup.cc:2925 #, c-format msgid "exceeded %d line entries" msgstr "превышение %d хранимых линий" #: f.meta.cc:180 f.widgets.cc:669 msgid "View Metadata" msgstr "Просмотр метаданных" #: f.meta.cc:430 f.meta.cc:1807 f.widgets.cc:412 f.widgets.cc:670 msgid "Edit Metadata" msgstr "Редактировать метаданные" #: f.meta.cc:433 f.meta.cc:3819 msgid "save metadata to file" msgstr "" #: f.meta.cc:442 msgid "Image Date" msgstr "Дата" #: f.meta.cc:445 msgid "Time" msgstr "Время" #: f.meta.cc:451 msgid "Rating (stars):" msgstr "Рейтинг:" #: f.meta.cc:463 msgid "Caption" msgstr "Подпись" #: f.meta.cc:469 msgid "Comments" msgstr "Комментарий" #: f.meta.cc:477 msgid "Enter New Tag" msgstr "" #: f.meta.cc:484 f.meta.cc:5003 msgid "Matching Tags" msgstr "" #: f.meta.cc:492 msgid "Image Tags" msgstr "Теги изображений" #: f.meta.cc:498 msgid "Recent Tags" msgstr "Последние теги" #: f.meta.cc:507 f.meta.cc:2029 f.meta.cc:5010 msgid "Defined Tags Category" msgstr "" #: f.meta.cc:898 msgid "date format is YYYY-MM-DD" msgstr "формат даты ГГГГ-ММ-ДД" #: f.meta.cc:902 msgid "date is invalid" msgstr "неверная дата" #: f.meta.cc:936 msgid "time format is HH:MM [:SS]" msgstr "формат времени ЧЧ:ММ[:СС]" #: f.meta.cc:940 msgid "time is invalid" msgstr "неверное время" #: f.meta.cc:1025 fotoxx.h:1133 msgid "Manage Tags" msgstr "Управление тегами" #: f.meta.cc:1025 msgid "orphan tags" msgstr "неиспользуемые тэги" #: f.meta.cc:1029 msgid "category" msgstr "Категория" #: f.meta.cc:1032 msgid "tag" msgstr "тег" #: f.meta.cc:1039 msgid "Defined Tags:" msgstr "Известные теги:" #: f.meta.cc:1811 f.meta.cc:2474 msgid "key name" msgstr "имя ключа" #: f.meta.cc:1813 f.meta.cc:2475 msgid "key value" msgstr "значение ключа" #: f.meta.cc:1900 f.widgets.cc:414 msgid "Delete Metadata" msgstr "Удалить метаданные" #: f.meta.cc:1902 fotoxx.h:1082 msgid "All" msgstr "Все" #: f.meta.cc:1903 msgid "One Key:" msgstr "Один ключ:" #: f.meta.cc:2006 f.widgets.cc:415 f.widgets.cc:540 msgid "Batch Add/Remove Tags" msgstr "Групповое добавление/удаление тэгов" #: f.meta.cc:2017 msgid "tags to add" msgstr "Теги для добавления" #: f.meta.cc:2018 msgid "tags to remove" msgstr "удаляемые тэги" #: f.meta.cc:2102 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" "слишком много тегов" #: f.meta.cc:2217 msgid "specify files and tags" msgstr "задайте файлы и тэги" #: f.meta.cc:2277 msgid "tag names file" msgstr "" #: f.meta.cc:2349 #, c-format msgid "" "%d tags to rename \n" "in %d image files. \n" "Proceed?" msgstr "" #: f.meta.cc:2461 msgid "Batch Metadata" msgstr "" #: f.meta.cc:2468 msgid "Short List" msgstr "" #: f.meta.cc:2469 msgid "Full List" msgstr "" #: f.meta.cc:2511 msgid "enter key names" msgstr "" #: f.meta.cc:2517 fotoxx.h:1141 msgid "no files selected" msgstr "нет выбранных файлов" #: f.meta.cc:2643 msgid "" "The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names" msgstr "" #: f.meta.cc:3276 f.meta.cc:4011 f.meta.cc:4277 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "Неправильная широта/долгота: %s %s" #: f.meta.cc:3320 f.meta.cc:3682 f.meta.cc:4486 f.meta.cc:4894 msgid "-noindex in use, disabled" msgstr "невозможно: запущено с -noindex" #: f.meta.cc:3336 msgid "choose map file" msgstr "выберите файл карты" #: f.meta.cc:3444 msgid "" "fotoxx-maps package not installed \n" "(see http://kornelix.com/packages and /tarballs)" msgstr "" #: f.meta.cc:3449 #, c-format msgid "map file %s is missing" msgstr "файл карты %s не найден" #: f.meta.cc:3453 #, c-format msgid "" "map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f" msgstr "" "данные широты/долготы бессмысленны \n" " %.3f %.3f %.3f %.3f" #: f.meta.cc:3731 msgid "No matching images found" msgstr "Соответствующие изображения не найдены" #: f.meta.cc:3771 msgid "search range (km)" msgstr "Диапазон поиска (км)" #: f.meta.cc:3804 f.widgets.cc:418 f.widgets.cc:671 msgid "Edit Geotags" msgstr "Редактировать геотеги" #: f.meta.cc:3805 msgid "Geocoding web service courtesy of" msgstr "Благодаря веб-сервису геокодирования" #: f.meta.cc:3821 f.meta.cc:4060 f.meta.cc:5783 msgid "city" msgstr "Город" #: f.meta.cc:3824 f.meta.cc:4063 f.meta.cc:5786 msgid "country" msgstr "Страна" #: f.meta.cc:3906 f.meta.cc:4194 msgid "city not found" msgstr "Город не найден" #: f.meta.cc:4027 f.widgets.cc:419 f.widgets.cc:543 msgid "Batch Add Geotags" msgstr "Групповое добавление геотегов" #: f.meta.cc:4256 msgid "" "data is incomplete \n" " proceed?" msgstr "" "Данные не полные \n" " Продолжить?" #: f.meta.cc:4313 msgid "choose city" msgstr "Выбрать город" #: f.meta.cc:4400 msgid "not found" msgstr "Не найден" #: f.meta.cc:4401 msgid "city and country required" msgstr "укажите город и страну" #: f.meta.cc:4501 msgid "Report Geotag Groups" msgstr "Сообщение группы геотегов" #: f.meta.cc:4502 msgid "Group by country" msgstr "Группировать по стране" #: f.meta.cc:4503 msgid "Group by country/city" msgstr "Группировать по стране/городу" #: f.meta.cc:4504 msgid "Group by country/city/date" msgstr "Группировать по стране/городу/дате" #: f.meta.cc:4507 msgid "Combine within" msgstr "Объединить в" #: f.meta.cc:4509 msgid "days" msgstr "Дней" #: f.meta.cc:4625 msgid "geotag groups" msgstr "группы геотегов" #: f.meta.cc:4931 msgid "Search Image Metadata" msgstr "Поиск  метаданных изображения" #: f.meta.cc:4935 msgid "images to search:" msgstr "изображения для поиска:" #: f.meta.cc:4936 msgid "all" msgstr "все" #: f.meta.cc:4937 msgid "current set only" msgstr "только активный набор" #: f.meta.cc:4940 msgid "matching images:" msgstr "подходящие изображения:" #: f.meta.cc:4941 msgid "new set" msgstr "новый набор" #: f.meta.cc:4942 msgid "add to set" msgstr "добавить в набор" #: f.meta.cc:4943 msgid "remove" msgstr "удалить" #: f.meta.cc:4946 msgid "report type:" msgstr "тип отчёта:" #: f.meta.cc:4947 msgid "gallery" msgstr "галерея" #: f.meta.cc:4948 fotoxx-15.11.cc:791 msgid "metadata" msgstr "метаданные" #: f.meta.cc:4954 msgid "date range" msgstr "Диапазон дат" #: f.meta.cc:4955 msgid "stars range" msgstr "Диапазон рейтинга" #: f.meta.cc:4956 msgid "search tags" msgstr "Поиск тегов" #: f.meta.cc:4957 msgid "search text" msgstr "Поиск текста" #: f.meta.cc:4958 msgid "search files" msgstr "Поиск файлов" #: f.meta.cc:4963 msgid "(yyyymmdd)" msgstr "(ггггммдд)" #: f.meta.cc:4968 msgid "last version only" msgstr "только последняя версия" #: f.meta.cc:4970 msgid "all/any" msgstr "Все/любой" #: f.meta.cc:4989 msgid "other criteria" msgstr "Другие критерии" #: f.meta.cc:4993 msgid "other" msgstr "Другое" #: f.meta.cc:4999 msgid "Enter Search Tag" msgstr "" #: f.meta.cc:5294 #, c-format msgid "not a defined tag: %s" msgstr "" #: f.meta.cc:5325 msgid "" "to remove images from current set, \n" "search current set" msgstr "" "для удаления изображений из активного набора, \n" "поиск активного набора" #: f.meta.cc:5332 msgid "" "to add images to current set, \n" "search all images" msgstr "" "для добавления изображений в активный набор \n" "поиск всех изображений" #: f.meta.cc:5376 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" #: f.meta.cc:5394 msgid "stars range not reasonable" msgstr "неверный диапазон рейтинга" #: f.meta.cc:5639 #, c-format msgid "images added: %d removed: %d new count: %d" msgstr "добавлено изображений: %d удалено: %d осталось: %d" #: f.meta.cc:5642 msgid "no changes made" msgstr "без изменений" #: f.meta.cc:5780 msgid "Add Geotags Search Criteria" msgstr "Добавить критерии поиска геотегов" #: f.meta.cc:5799 msgid "range (km)" msgstr "Расстояние (км)" #: f.meta.cc:5892 msgid "error in latitude/longitude/range" msgstr "Ошибка в широте/долготе/расстоянии" #: f.meta.cc:5956 msgid "" "These items are always reported: \n" "date, stars, tags, caption, comment" msgstr "" "Всегда предоставляются: \n" "дата, рейтинг, теги, подпись, комментарии" #: f.meta.cc:5980 msgid "Additional Items for Report" msgstr "Дополнительные пункты для отчета" #: f.meta.cc:5986 msgid "Keyword" msgstr "Ключевое слово" #: f.meta.cc:5993 msgid "Match Criteria" msgstr "Критерии подбора" #: f.meta.cc:6553 msgid "image index is missing" msgstr "нет индекса изображений" #: f.repair.cc:1041 msgid "Apply repeatedly while watching the image." msgstr "" #: f.repair.cc:1042 msgid "Measure" msgstr "" #: f.repair.cc:1078 msgid "Noise Reduction" msgstr "Подавление шума" #: f.repair.cc:1099 msgid "Flatten 1" msgstr "" #: f.repair.cc:1100 msgid "Flatten 2" msgstr "" #: f.repair.cc:1101 msgid "Median" msgstr "" #: f.repair.cc:1121 msgid "dark areas" msgstr "тёмные зоны" #: f.repair.cc:1123 msgid "all areas" msgstr "все зоны" #: f.repair.cc:1246 msgid "Measure Noise" msgstr "" #: f.repair.cc:1247 msgid "Click on a monotone image area." msgstr "" #: f.repair.cc:2177 msgid "" "1. Drag mouse to select. \n" "2. Erase. 3. Repeat. " msgstr "" "1. Передвинуть мышь для выбора. \n" "2. Стереть. 3. Повторить. " #: f.repair.cc:2199 f.widgets.cc:457 msgid "Smart Erase" msgstr "Умное стирание" #: f.repair.cc:2204 fotoxx.h:1152 msgid "Radius" msgstr "Радиус" #: f.repair.cc:2206 f.widgets.cc:455 msgid "Blur" msgstr "Размыть" #: f.repair.cc:2209 msgid "New Area" msgstr "Новая область" #: f.repair.cc:2558 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 "" " Метод 1:\n" " Кликните левой кнопкой мышки на красных глазах.\n" " Метод 2:\n" " Движение мышью вниз и вправо, чтобы выделить красные глаза.\n" " Кликните левой кнопкой мышки на красных глазах.\n" " Для отмены:\n" " Кликните правой кнопкой мышки на красных глазах." #: f.repair.cc:2574 msgid "Red Eye Reduction" msgstr "Снижение эффекта красных глаз" #: f.repair.cc:3038 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 "" "Shift + левый клик: задать цвет или расположение \n" "Левый клик/перемещение: рисовать цветом / копировать фрагмент \n" "Правый клик/перемещение: удаление цвета/фрагмента" #: f.repair.cc:3070 f.widgets.cc:459 msgid "Paint/Clone" msgstr "Перекрасить пиксели" #: f.repair.cc:3074 msgid "paint color" msgstr "Рисовать цветом" #: f.repair.cc:3077 msgid "copy from image" msgstr "Копировать из изображения" #: f.repair.cc:3084 f.repair.cc:3641 msgid "paintbrush radius" msgstr "Радиус кисти" #: f.repair.cc:3085 msgid "transparency center" msgstr "Прозрачность центра" #: f.repair.cc:3086 msgid "transparency edge" msgstr "Прозрачность края" #: f.repair.cc:3093 f.repair.cc:3648 msgid "gradual paint" msgstr "закрашивание" #: f.repair.cc:3100 #, c-format msgid "Undo Memory %d%c" msgstr "Память отмены %d%c" #: f.repair.cc:3436 msgid "" "Undo memory limit has been reached. \n" "Save work with [done], then resume painting." msgstr "" "Был достигнут предел памяти отмены. \n" "Нажмите [Изменить] для сохранения изменений, затем продолжите рисование." #: f.repair.cc:3601 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" #: f.repair.cc:3633 f.widgets.cc:460 msgid "Paint Transparency" msgstr "" #: f.repair.cc:3642 msgid "strength center" msgstr "" #: f.repair.cc:3643 msgid "strength edge" msgstr "" #: f.repair.cc:3858 f.widgets.cc:461 msgid "Color Mode" msgstr "Цветовые режимы" #: f.repair.cc:3862 msgid "black/white positive" msgstr "Черно-белый позитив" #: f.repair.cc:3863 msgid "black/white negative" msgstr "Черно-белый негатив" #: f.repair.cc:3864 msgid "color positive" msgstr "Цветной позитив" #: f.repair.cc:3865 msgid "color negative" msgstr "Цветной негатив" #: f.repair.cc:3866 msgid "sepia" msgstr "Сепия" #: f.repair.cc:4046 f.widgets.cc:462 msgid "Shift Colors" msgstr "Исправить цвет" #: f.repair.cc:4317 msgid "+Brightness" msgstr "+Яркость" #: f.repair.cc:4318 msgid "+Red -Cyan" msgstr "+Красный -Голубой" #: f.repair.cc:4319 msgid "+Green -Magenta" msgstr "+Зеленый -Пурпурный" #: f.repair.cc:4320 msgid "+Blue -Yellow" msgstr "+Синий -Желтый" #: f.repair.cc:4326 fotoxx.h:1154 msgid "Red" msgstr "Красный" #: f.repair.cc:4327 fotoxx.h:1120 msgid "Green" msgstr "Зеленый" #: f.repair.cc:4328 fotoxx.h:1089 msgid "Blue" msgstr "Синий" #: f.repair.cc:4623 msgid "Color to change" msgstr "" #: f.repair.cc:4625 msgid "shift+click on image to select color" msgstr "" #: f.repair.cc:4638 msgid "Color Hue" msgstr "" #: f.repair.cc:4639 msgid "Saturation" msgstr "" #: f.repair.cc:4640 msgid "Lightness" msgstr "" #: f.repair.cc:4641 msgid "Color Match" msgstr "" #: f.repair.cc:4642 msgid "Adjustment" msgstr "" #: f.repair.cc:5028 msgid "Ramp brightness across image" msgstr "Смещение яркости по изображению" #: f.repair.cc:5352 f.tools.cc:2048 msgid "Click image to select pixels." msgstr "Кликните изображение для выбора пикселей." #: f.repair.cc:5383 f.widgets.cc:466 msgid "Color Ramp" msgstr "" #: f.repair.cc:5394 msgid "Metric:" msgstr "Метрика:" #: f.repair.cc:5817 msgid "Color Match Images" msgstr "Подобрать цвет изображений" #: f.repair.cc:5843 msgid "mouse radius for color sample" msgstr "Радиус мыши для образца цвета" #: f.repair.cc:5846 msgid "image for source color" msgstr "Изображениедля источника цвета" #: f.repair.cc:5848 msgid "click on image to get source color" msgstr "Кликните по изображению для получения исходного цвета" #: f.repair.cc:5851 msgid "image to set matching color" msgstr "Изображение для подбора цвета" #: f.repair.cc:5853 msgid "click on image to set matching color" msgstr "Кликните по изображению для подбора цвета" #: f.repair.cc:5914 msgid "select source image color first" msgstr "Сначала выберите исходный цвет" #: f.repair.cc:6099 msgid "Change Color Profile" msgstr "Изменение цветового профиля" #: f.repair.cc:6103 msgid "input profile" msgstr "Профиль ввода" #: f.repair.cc:6107 msgid "output profile" msgstr "профиль вывода" #: f.repair.cc:6123 msgid "color profile" msgstr "профиль цветов" #: f.repair.cc:6169 f.repair.cc:6175 #, c-format msgid "unknown cms profile %s" msgstr "неизвестный профиль cms %s" #: f.repair.cc:6307 f.widgets.cc:469 msgid "Remove Dust" msgstr "Удалить пыль" #: f.repair.cc:6311 msgid "spot size limit" msgstr "Предельный размер пятна" #: f.repair.cc:6314 msgid "max. brightness" msgstr "Макс. яркость" #: f.repair.cc:6317 msgid "min. contrast" msgstr "Мин. контраст" #: f.repair.cc:7098 msgid "" " Adjust each RGB color to minimize \n" " color fringes at the image extremes. " msgstr "" "Отрегулируйте каждый цвет RGB для уменьшения \n" "ореола по краям." #: f.repair.cc:7121 f.widgets.cc:471 msgid "Color Fringes" msgstr "" #: f.repair.cc:7320 f.widgets.cc:472 msgid "Stuck Pixels" msgstr "" #: f.repair.cc:7326 msgid "pixel group" msgstr "Группа пикселей" #: f.repair.cc:7334 f.repair.cc:7402 msgid "stuck pixels:" msgstr "горячие пиксели:" #: f.repair.cc:7430 msgid "Load Stuck Pixels" msgstr "Загрузка горячих пикселей" #: f.repair.cc:7432 msgid "File:" msgstr "Файл:" #: f.repair.cc:7457 f.repair.cc:7514 msgid "Stuck Pixels file" msgstr "Файл горячих пикселей" #: f.repair.cc:7494 msgid "file format error" msgstr "Ошибка формата файла" #: f.tools.cc:70 msgid "" "Select directories containing image files \n" "(subdirectories are included automatically)." msgstr "" #: f.tools.cc:72 msgid "Select to add, click on X to delete." msgstr "" #: f.tools.cc:73 msgid "Select directory for thumbnails." msgstr "" #: f.tools.cc:74 msgid "" "Index function terminated. Fotoxx will close. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" #: f.tools.cc:106 f.widgets.cc:513 msgid "Index Image Files" msgstr "Индекс файлов изображений" #: f.tools.cc:250 msgid "Choose top image directories" msgstr "Выбрать папку верхнего уровня" #: f.tools.cc:251 msgid "Choose thumbnail directory" msgstr "Выберите папку верхнего уровня" #: f.tools.cc:324 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 "" #: f.tools.cc:330 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" #: f.tools.cc:333 #, c-format msgid "" "Thumbnails directory: \n" " %s \n" "must be named .../thumbnails" msgstr "" #: f.tools.cc:336 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" #: f.tools.cc:385 msgid "no thumbnails directory defined" msgstr "каталог миниатюр не определён" #: f.tools.cc:735 msgid "COMPLETED" msgstr "ЗАВЕРШЕНО" #: f.tools.cc:776 msgid "Indexing is required for first-time startup." msgstr "" #: f.tools.cc:838 msgid "Recent Files Gallery" msgstr "Галерея недавно открытых файлов" #: f.tools.cc:839 msgid "Newest Files Gallery" msgstr "Галерея Свежих Файлов" #: f.tools.cc:840 msgid "Previous Gallery" msgstr "Предыдущая Галерея" #: f.tools.cc:841 msgid "Previous Image" msgstr "Предыдущее Изображение" #: f.tools.cc:842 msgid "Blank Window" msgstr "Пустое окно" #: f.tools.cc:843 msgid "Directory" msgstr "" #: f.tools.cc:844 msgid "Image File" msgstr "Файл изображения" #: f.tools.cc:887 f.widgets.cc:514 msgid "User Options" msgstr "" #: f.tools.cc:890 msgid "Startup Display" msgstr "Начальный экран" #: f.tools.cc:901 msgid "Menu Style" msgstr "Стиль Меню" #: f.tools.cc:902 msgid "Icons" msgstr "Значки" #: f.tools.cc:903 msgid "Icons + Text" msgstr "Иконки + Текст" #: f.tools.cc:905 msgid "Icon size" msgstr "Размер иконок" #: f.tools.cc:909 msgid "Dialog font and size" msgstr "" #: f.tools.cc:914 msgid "Image Pan/scroll:" msgstr "" #: f.tools.cc:917 msgid "Zooms for 2x" msgstr "Увеличение для 2х" #: f.tools.cc:926 msgid "JPEG save quality" msgstr "Качество сохраняемого JPEG" #: f.tools.cc:933 msgid "Curve node capture distance" msgstr "" #: f.tools.cc:937 msgid "show hidden directories in gallery view" msgstr "" #: f.tools.cc:940 msgid "prev/next shows last file version only" msgstr "вперёд/назад показывает только последние версии" #: f.tools.cc:943 msgid "RAW command" msgstr "Команда RAW" #: f.tools.cc:947 msgid "RAW file types" msgstr "Типы файлов RAW" #: f.tools.cc:1029 msgid "Delete present thumbnails to make effective" msgstr "Удалите существующие иконки для смены размера" #: f.tools.cc:1051 msgid "Select startup directory" msgstr "Выбрать начальный каталог" #: f.tools.cc:1058 msgid "Select startup image file" msgstr "Выбрать начальный файл изображения" #: f.tools.cc:1096 msgid "startup directory is invalid" msgstr "Некорректный начальный каталог" #: f.tools.cc:1107 msgid "startup file is invalid" msgstr "Некорректный начальный файл" #: f.tools.cc:1247 msgid "Edit KB Shortcuts" msgstr "Редактировать горячие клавиши" #: f.tools.cc:1252 msgid "shortcut key:" msgstr "горячая клавиша:" #: f.tools.cc:1253 msgid "(enter key)" msgstr "(клавиша ввод)" #: f.tools.cc:1379 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Зарезервировано: переопределить невозможно" #: f.tools.cc:1533 msgid "unable to save KB-shortcuts file" msgstr "невозможно сохранить файл горячих клавиш" #: f.tools.cc:1837 f.widgets.cc:517 msgid "Grid Lines" msgstr "Линии сетки" #: f.tools.cc:1846 msgid "x-spacing" msgstr "x-интервал" #: f.tools.cc:1847 msgid "x-count" msgstr "x-значение" #: f.tools.cc:1848 msgid "x-enable" msgstr "x-включить" #: f.tools.cc:1854 msgid "y-spacing" msgstr "y-интервал" #: f.tools.cc:1855 msgid "y-count" msgstr "y-значение" #: f.tools.cc:1856 msgid "y-enable" msgstr "y-включить" #: f.tools.cc:1976 f.tools.cc:1981 f.widgets.cc:518 msgid "Line Color" msgstr "Цвет Линии" #: f.tools.cc:1978 msgid "Area Color" msgstr "Цвет области" #: f.tools.cc:2083 f.widgets.cc:519 msgid "Show RGB" msgstr "Показать RGB" #: f.tools.cc:2375 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key M to toggle dialog." msgstr "" "Выберите область мышкой \n" "Левый щелчок для отмены \n" "M включает/выключает диалог" #: f.tools.cc:2401 f.widgets.cc:520 msgid "Magnify Image" msgstr "Лупа" #: f.tools.cc:2410 msgid "X-size" msgstr "Увеличение" #: f.tools.cc:2655 msgid "Darkest and Brightest Pixels" msgstr "Тёмные и яркие пиксели" #: f.tools.cc:2678 msgid "Dark Limit" msgstr "Граница тёмных" #: f.tools.cc:2679 msgid "Bright Limit" msgstr "Граница светлых" #: f.tools.cc:2774 msgid "" "Brightness should show a gradual ramp \n" "extending all the way to the edges." msgstr "" "Яркость должна постепенно изменяться \n" "в направлении краев." #: f.tools.cc:2870 f.widgets.cc:523 msgid "Monitor Gamma" msgstr "Гамма монитора" #: f.tools.cc:2930 msgid "Available Translations" msgstr "Доступные переводы" #: f.tools.cc:2934 msgid "Set Language" msgstr "Установка языка" #: f.tools.cc:3071 f.widgets.cc:526 msgid "Calibrate Printer" msgstr "" #: f.tools.cc:3096 msgid "print color chart" msgstr "" #: f.tools.cc:3097 msgid "scan and save color chart" msgstr "" #: f.tools.cc:3098 msgid "align and trim color chart" msgstr "" #: f.tools.cc:3099 msgid "open and process color chart" msgstr "" #: f.tools.cc:3100 msgid "print image with revised colors" msgstr "" #: f.tools.cc:3264 #, c-format msgid "" "Scan the printed color chart. \n" "Save in %s/" msgstr "" #: f.tools.cc:3278 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation caused by the scanner. \n" "Trim off all margins. Be careful not to cut off \n" "any color tile edges or leave any white margins." msgstr "" #: f.tools.cc:3310 msgid "Open the trimmed color chart file" msgstr "" #: f.tools.cc:3443 msgid "" "Set the name for the output calibration file \n" "[your calibration name].dat" msgstr "" #: f.tools.cc:3483 msgid "Color map file to use" msgstr "" #: f.tools.cc:3500 msgid "Select the image file to print." msgstr "" #: f.tools.cc:3565 msgid "converting colors..." msgstr "" #: f.tools.cc:3665 msgid "Image colors are converted for printing." msgstr "" #: f.widgets.cc:111 msgid "Album" msgstr "" #: f.widgets.cc:113 msgid "TOP" msgstr "ВЕРХ" #: f.widgets.cc:165 msgid "Current Image File (key F)" msgstr "Текущий файл изображения (F)" #: f.widgets.cc:166 msgid "Thumbnail Gallery (key G)" msgstr "Галерея иконок (G)" #: f.widgets.cc:167 msgid "World Maps (key W)" msgstr "Карта мира (W)" #: f.widgets.cc:170 msgid "Favorite Functions" msgstr "Любимые функции" #: f.widgets.cc:171 msgid "File: Open, RAW, Rename, Trash, Print" msgstr "" #: f.widgets.cc:172 msgid "Save modified image file to disk" msgstr "Сохранить изменённое изображение на диск" #: f.widgets.cc:173 msgid "Open previous or next file (left/right mouse click)" msgstr "Открыть предыдущий/следующий файл (лев/прав клик)" #: f.widgets.cc:174 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "Метаданные: Комментарии, Теги, Рейтинг, Поиск..." #: f.widgets.cc:175 msgid "Areas: Select areas to edit, copy and paste" msgstr "Области: выбор областей для редактирования, копирования/вставки" #: f.widgets.cc:176 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "Редактирование: Обрезка, Поворот, Размер, Яркость, Контраст, Текст...." #: f.widgets.cc:177 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "" "Улучшение: Резкость, Шум, Красные глаза, Цвет, Рисование, Клонирование..." #: f.widgets.cc:178 msgid "Bend: Fix Perspective, Bend/Warp image ..." msgstr "Деформация: Перспектива, Деформации..." #: f.widgets.cc:179 msgid "Effects: Special Effects, Arty Transforms" msgstr "Эффекты: Художественная трансформация, Спецэффекты" #: f.widgets.cc:180 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "Комбинирование: HDR, HDF, Панорама, Стек, Коллаж" #: f.widgets.cc:181 msgid "" "Undo or Redo one edit (left/right mouse click) \n" " hold key A to include all edits" msgstr "" "Отменить/повторить операцию (лев/прав клик) \n" " удерживайте A для всех операций" #: f.widgets.cc:183 msgid "Tools: Index, Options, Shortcuts, Magnify ..." msgstr "" #: f.widgets.cc:184 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "Помощь: Быстрый старт, Руководство Пользователя, Новые возможности" #: f.widgets.cc:187 msgid "Set gallery from current image file" msgstr "Задать галерею с текущего изображения" #: f.widgets.cc:188 msgid "Albums: Manage Albums, Slide Show, Desktop Wallpaper" msgstr "" #: f.widgets.cc:189 msgid "go to bookmarked image" msgstr "перейти по закладке" #: f.widgets.cc:190 msgid "increase thumbnail size" msgstr "Увеличение размера миниатюры" #: f.widgets.cc:191 msgid "reduce thumbnail size" msgstr "Уменьшить размер миниатюр" #: f.widgets.cc:192 msgid "change sort order" msgstr "изменить порядок сортировки" #: f.widgets.cc:193 msgid "jump to beginning (top)" msgstr "на начало" #: f.widgets.cc:194 msgid "jump to end (bottom)" msgstr "в конец" #: f.widgets.cc:195 msgid "previous page" msgstr "Предыдущая страница" #: f.widgets.cc:196 msgid "next page" msgstr "Следующая страница" #: f.widgets.cc:197 msgid "slow scroll" msgstr "медленный скролл" #: f.widgets.cc:198 msgid "Batch conversion, metadata, RAW processing" msgstr "" #: f.widgets.cc:201 msgid "Choose a map for image locations" msgstr "Выберите карту для позиционирования фото" #: f.widgets.cc:202 msgid "Set image search radius for map click" msgstr "Установите радиус поиска при клике по карте" #: f.widgets.cc:205 msgid "Open another window" msgstr "Открыть другое окно" #: f.widgets.cc:206 msgid "Open a new image file" msgstr "Открыть новый файл изображения" #: f.widgets.cc:207 msgid "Open the previously seen file" msgstr "Открыть ранее просмотренный файл" #: f.widgets.cc:208 msgid "Open a recently seen file" msgstr "Открыть недавно просмотренный файл" #: f.widgets.cc:209 msgid "Open a newly added file" msgstr "Открыть добавленный файл" #: f.widgets.cc:210 f.widgets.cc:211 msgid "Open and edit a camera RAW file" msgstr "Открыть и редактировать RAW файл" #: f.widgets.cc:212 msgid "Change the image file name" msgstr "Переименовать файл изображения" #: f.widgets.cc:213 msgid "Create a blank image" msgstr "Создать пустое изображение" #: f.widgets.cc:214 msgid "Move image file to Trash" msgstr "Переместить файл изображения в корзину?" #: f.widgets.cc:215 msgid "Permanently delete image file" msgstr "Удалить файл изображения навсегда" #: f.widgets.cc:216 msgid "Print the current image" msgstr "Печатать текущее изображения" #: f.widgets.cc:217 msgid "Print current image with adjusted colors" msgstr "" #: f.widgets.cc:218 f.widgets.cc:406 msgid "Quit Fotoxx" msgstr "Выход из fotoxx" #: f.widgets.cc:221 msgid "List a few key metadata items" msgstr "Список ключевых элементов метаданных" #: f.widgets.cc:222 msgid "List all metadata items" msgstr "Список всех элементов метаданных" #: f.widgets.cc:223 msgid "(Toggle) show captions and comments" msgstr "(Переключение) показать подписи/комментарии" #: f.widgets.cc:224 msgid "Edit image tags/caption/rating ..." msgstr "Редактировать теги/рейтинг/описание" #: f.widgets.cc:225 msgid "Edit any image metadata" msgstr "Редактировать метаданные изображения" #: f.widgets.cc:226 msgid "Remove all metadata from an image" msgstr "Удалить все метаданные из изображения" #: f.widgets.cc:227 msgid "Add/remove tags for multiple images" msgstr "Групповое изменение тэгов" #: f.widgets.cc:228 msgid "Convert tag names for all images" msgstr "" #: f.widgets.cc:229 msgid "Add/change/delete metadata for multiple images" msgstr "" #: f.widgets.cc:230 msgid "Edit image location and geotags" msgstr "Редактировать геотеги " #: f.widgets.cc:231 msgid "Add/revise geotags for multiple images" msgstr "" #: f.widgets.cc:232 msgid "Find all images for a location [date]" msgstr "Найти все изображения по расположению [дате]" #: f.widgets.cc:233 msgid "Find images meeting select criteria" msgstr "Найти изображения, соответствующие выбранным критериям" #: f.widgets.cc:236 msgid "Select object or area for editing" msgstr "Выберите объект или область для редактирования" #: f.widgets.cc:237 msgid "Show (outline) existing area" msgstr "Показать (контур) существующую область" #: f.widgets.cc:238 msgid "Hide existing area" msgstr "Скрыть существующую область" #: f.widgets.cc:239 msgid "Enable area for editing" msgstr "Включить область для последующих изменений" #: f.widgets.cc:240 msgid "Disable area for editing" msgstr "Временно исключить область из списка измененяемых" #: f.widgets.cc:241 msgid "Reverse existing area" msgstr "Инвертировать существующую область" #: f.widgets.cc:242 msgid "Erase existing area" msgstr "Стереть существующую область" #: f.widgets.cc:243 msgid "Copy area for later pasting into image" msgstr "" #: f.widgets.cc:244 msgid "Paste previously copied area into image" msgstr "" #: f.widgets.cc:245 msgid "Open a file and paste as area into image" msgstr "" #: f.widgets.cc:246 msgid "Save area to a file with transparency" msgstr "" #: f.widgets.cc:249 msgid "Trim/Crop margins and/or Rotate" msgstr "Границы при обрезке и/или повороте" #: f.widgets.cc:250 msgid "Upright a rotated image" msgstr "Поворот изображения вертикально" #: f.widgets.cc:251 f.widgets.cc:252 msgid "Fast auto enhance that may work OK" msgstr "Быстрое авто улучшение (может сработать хорошо)" #: f.widgets.cc:253 msgid "Adjust brightness, contrast, color" msgstr "Настроить яркость, контрастность, цветность" #: f.widgets.cc:254 msgid "Add local contrast, enhance details" msgstr "Добавить локальный контраст, усилить детали" #: f.widgets.cc:255 msgid "Adjust brightness distribution" msgstr "Выровнять распределение яркости" #: f.widgets.cc:256 msgid "Flatten zonal brightness distribution" msgstr "" #: f.widgets.cc:257 msgid "Change pixel dimensions" msgstr "Изменение размеров в пикселах" #: f.widgets.cc:258 msgid "Mirror image horizontally or vertically" msgstr "Зеркальное отражение по горизонтали или вертикали" #: f.widgets.cc:259 msgid "Write text on image" msgstr "Запись текста на изображение" #: f.widgets.cc:260 msgid "Write lines or arrows on image" msgstr "Начертить линии или стрелки" #: f.widgets.cc:261 msgid "Fix brightness uniformity across image" msgstr "Исправить неоднородность яркости по изображению" #: f.widgets.cc:262 msgid "Paint edit function gradually with mouse" msgstr "Закрашивайте движениями мышки" #: f.widgets.cc:263 msgid "Leverage edits by brightness or color" msgstr "Усиление/ослабление функции в зависимости от яркости или цвета" #: f.widgets.cc:264 msgid "Edit plugins menu or run a plugin function" msgstr "Редактировать меню плагинов или запустить плагин" #: f.widgets.cc:267 msgid "Make the image look sharper" msgstr "Повысить чёткость изображения" #: f.widgets.cc:268 msgid "Make the image look fuzzy" msgstr "Размыть изображение " #: f.widgets.cc:269 msgid "Filter noise from low-light photos" msgstr "Фильтрация шума в темных изображениях" #: f.widgets.cc:270 msgid "Remove unwanted objects" msgstr "Удаление нежелательных объектов" #: f.widgets.cc:271 msgid "Fix red-eyes from electronic flash" msgstr "Исправить эффект красных глаз при вспышке" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Окраска пикселей мышью" #: f.widgets.cc:273 msgid "Paint image transparency using the mouse" msgstr "" #: f.widgets.cc:274 msgid "Make BW/color, negative/positive, sepia" msgstr "Преобразовать Ч/Б/цвет, негатив/позитив, сепия" #: f.widgets.cc:275 msgid "Shift/convert colors into other colors" msgstr "Сдвиг/преобразования цветов" #: f.widgets.cc:276 msgid "Adjust color using RGB or CMY colors" msgstr "" #: f.widgets.cc:277 msgid "Adjust color using HSL colors" msgstr "" #: f.widgets.cc:278 msgid "Adjust color in selected image areas" msgstr "Настройка цвета в выбранной области изображения" #: f.widgets.cc:279 msgid "Match colors on one image with another" msgstr "Совмещение цветов одного изображения с другим" #: f.widgets.cc:280 msgid "Convert to another color profile" msgstr "Преобразование в другой профиль цветов" #: f.widgets.cc:281 msgid "Remove dust spots from scanned slides" msgstr "Удаление пятен пыли из отсканированных слайдов" #: f.widgets.cc:282 msgid "Smoothen edges with jaggies" msgstr "Спрямить зазубренные края" #: f.widgets.cc:283 msgid "Reduce Chromatic Abberation" msgstr "Коррекция хроматической аберрации" #: f.widgets.cc:284 msgid "Erase known hot and dark pixels" msgstr "Стереть известные горячие и темные пиксели" #: f.widgets.cc:287 msgid "Remove curvature, esp. panoramas" msgstr "Удаление искривления, особенно панорам" #: f.widgets.cc:288 msgid "Straighten objects seen from an angle" msgstr "Выравнивание геометрии объектов, снятых под углом" #: f.widgets.cc:289 msgid "Distort image areas using the mouse" msgstr "Исказить области изображения мышкой" #: f.widgets.cc:290 f.widgets.cc:291 f.widgets.cc:292 msgid "Distort the whole image using the mouse" msgstr "Исказить всё изображение мышкой" #: f.widgets.cc:293 msgid "Flatten a photographed book page" msgstr "Выровнять сфотографированную книжную страницу" #: f.widgets.cc:296 msgid "Reduce color depth (posterize)" msgstr "Уменьшение глубины цвета (эффект плаката)" #: f.widgets.cc:297 msgid "Convert to pencil sketch" msgstr "Преобразовать в карандашный набросок" #: f.widgets.cc:298 msgid "Convert to line drawing (edge detection)" msgstr "Преобразовать в графику (распознавание границ)" #: f.widgets.cc:299 msgid "Convert to solid color drawing" msgstr "Преобразовать в цветной рисунок" #: f.widgets.cc:300 msgid "Graduated Blur depending on contrast" msgstr "Размытие в зависимости от контраста" #: f.widgets.cc:301 msgid "Create an embossed or 3D appearance" msgstr "Создать рельеф или видимость 3D" #: f.widgets.cc:302 msgid "Convert to square tiles" msgstr "Преобразование в квадратные плитки" #: f.widgets.cc:303 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "Преобразование в точки (эффект Roy Lichtenstein)" #: f.widgets.cc:304 msgid "Convert into a simulated painting" msgstr "Преобразование в моделируемую картину" #: f.widgets.cc:305 msgid "Change brightness or color radially" msgstr "Изменение яркости и цвета радиально" #: f.widgets.cc:306 msgid "Add texture to an image" msgstr "Добавить текстуру" #: f.widgets.cc:307 msgid "Tile image with a repeating pattern" msgstr "Замостить изображение повторяющимся шаблоном" #: f.widgets.cc:308 msgid "Create a mosaic with tiles made from all images" msgstr "Создать мозаику изо всех изображений" #: f.widgets.cc:309 msgid "Process an image using a custom kernel" msgstr "Матричное преобразование изображение" #: f.widgets.cc:310 msgid "Warp an image with a wave pattern" msgstr "Добавить волнистость" #: f.widgets.cc:311 msgid "Blur an image in the direction of mouse drags" msgstr "" #: f.widgets.cc:312 msgid "Make a spherical projection of an image" msgstr "" #: f.widgets.cc:315 msgid "Combine bright/dark images for better detail" msgstr "Объединение ярких/темных изображений для улучшения деталей" #: f.widgets.cc:316 msgid "Combine near/far focus images for deeper focus" msgstr "" "Объединение близко/далеко фокусных изображений для более глубокого фокуса" #: f.widgets.cc:317 msgid "Combine images to erase passing people, etc." msgstr "Объединение изображений чтобы стереть проходящих людей и т.д." #: f.widgets.cc:318 msgid "Combine noisy images into a low-noise image" msgstr "Объединение изображений с шумом и изображений с малым шумом" #: f.widgets.cc:319 msgid "Combine images into a panorama" msgstr "Объединение изображений в панораму" #: f.widgets.cc:320 msgid "Combine images into a vertical panorama" msgstr "Объединение изображений в вертикальную панораму" #: f.widgets.cc:321 msgid "Combine images into a panorama (panorama tools)" msgstr "Объединение изображений в панораму (panorama tools)" #: f.widgets.cc:322 msgid "Arrange images and text in a layout (montage)" msgstr "" #: f.widgets.cc:325 msgid "Index new files and make thumbnails" msgstr "Индексировать новые файлы и сделать миниатюры" #: f.widgets.cc:326 msgid "Change user preferences" msgstr "Изменение пользовательских настроек" #: f.widgets.cc:327 msgid "Change Keyboard Shortcut Keys" msgstr "Изменить горячие клавиши" #: f.widgets.cc:328 msgid "Show a brightness distribution graph" msgstr "Показать график распределения яркости" #: f.widgets.cc:329 msgid "Show or revise grid lines" msgstr "Показать или изменить линии сетки" #: f.widgets.cc:330 msgid "Change color of foreground lines" msgstr "Изменить цвет линий переднего плана" #: f.widgets.cc:331 msgid "Show RGB colors at mouse click" msgstr "Показать цвета RGB при щелчке мышью" #: f.widgets.cc:332 msgid "Magnify image around the mouse position" msgstr "Увеличить часть изображения под мышью" #: f.widgets.cc:333 msgid "Highlight darkest and brightest pixels" msgstr "Подсветка слишком тёмных или ярких пикселей" #: f.widgets.cc:334 msgid "Chart to adjust monitor color" msgstr "График для настройки цвета монитора" #: f.widgets.cc:335 msgid "Chart to adjust monitor gamma" msgstr "График для настройки гаммы монитора" #: f.widgets.cc:336 msgid "Change the GUI language" msgstr "Изменение языка GUI" #: f.widgets.cc:337 msgid "Report missing translations" msgstr "Сообщить о пропущенном переводе" #: f.widgets.cc:338 msgid "Calibrate printer colors" msgstr "" #: f.widgets.cc:339 msgid "Memory and CPU (to terminal/logfile)" msgstr "Память и ЦПУ (в терминал/лог файл)" #: f.widgets.cc:343 msgid "Rename/convert/resize/move multiple files" msgstr "Переименовать/преобразовать/переместить группу файлов" #: f.widgets.cc:344 msgid "Upright multiple rotated image files" msgstr "Пакетный поворот изображений вертикально" #: f.widgets.cc:345 msgid "Delete or Trash multiple files" msgstr "" #: f.widgets.cc:346 msgid "Convert camera RAW files using DCraw" msgstr "Преобразование RAW посредством DCRaw" #: f.widgets.cc:347 msgid "Convert camera RAW files using Raw Therapee" msgstr "Преобразование RAW посредством Raw Therapee" #: f.widgets.cc:348 msgid "Build and run edit script files" msgstr "" #: f.widgets.cc:349 msgid "Burn selected image files to CD or DVD" msgstr "Записать изображения на CD/DVD" #: f.widgets.cc:350 msgid "Search all image files and report duplicates" msgstr "Просматривает все файлы в поисках дублей" #: f.widgets.cc:354 msgid "Quick Start mini-guide" msgstr "Руководство по быстрому старту" #: f.widgets.cc:355 msgid "Read the user guide" msgstr "Причитать руководство пользователя" #: f.widgets.cc:356 msgid "Recent user guide changes" msgstr "Последние изменения руководства пользователя" #: f.widgets.cc:357 msgid "Summary of image edit functions" msgstr "Обзор функций для редактирования изображений" #: f.widgets.cc:358 msgid "Technical installation notes" msgstr "Технические замечания по установке" #: f.widgets.cc:359 msgid "List updates by Fotoxx version" msgstr "Список обновлений версии Fotoxx" #: f.widgets.cc:360 msgid "View the log file and error messages" msgstr "Просмотр файла журнала и сообщений об ошибках" #: f.widgets.cc:361 msgid "How to do Fotoxx translations" msgstr "Как сделать перевод Fotoxx" #: f.widgets.cc:362 msgid "Show the Fotoxx web page" msgstr "Показать веб страницу Fotoxx" #: f.widgets.cc:363 msgid "Version, license, contact, credits" msgstr "Версия, лицензия, контакты, кредиты" #: f.widgets.cc:366 msgid "Organize images into albums" msgstr "Рассортировать изображения по альбомам" #: f.widgets.cc:367 msgid "Start a slide show" msgstr "Запустить слайд-шоу" #: f.widgets.cc:368 msgid "Set desktop wallpaper from current Fotoxx image" msgstr "" #: f.widgets.cc:369 msgid "Cycle desktop wallpaper from a Fotoxx album" msgstr "" #: f.widgets.cc:391 msgid "New Window" msgstr "Новое окно" #: f.widgets.cc:392 msgid "Sync Gallery" msgstr "" #: f.widgets.cc:393 msgid "Recently Seen Images" msgstr "Недавно просмотренные изображения" #: f.widgets.cc:394 msgid "Newest Images" msgstr "Последние сообщения" #: f.widgets.cc:396 msgid "Open Previous File" msgstr "Открыть предыдущий файл" #: f.widgets.cc:397 msgid "Open RAW (ufraw)" msgstr "" #: f.widgets.cc:398 msgid "Open RAW (Raw Therapee)" msgstr "" #: f.widgets.cc:399 msgid "New Blank Image" msgstr "Новое пустое изображение" #: f.widgets.cc:402 msgid "Delete Image File" msgstr "Удалить файл изображения" #: f.widgets.cc:403 msgid "Print Image" msgstr "Печать изображения" #: f.widgets.cc:404 msgid "Print Calibrated Image" msgstr "" #: f.widgets.cc:405 f.widgets.cc:550 msgid "Set Desktop Wallpaper" msgstr "" #: f.widgets.cc:409 msgid "View Metadata (short)" msgstr "Просмотр метаданных (коротко)" #: f.widgets.cc:410 msgid "View Metadata (long)" msgstr "Просмотр метаданных (полностью)" #: f.widgets.cc:411 msgid "Show Captions on Image" msgstr "Показать подписи на изображении" #: f.widgets.cc:413 msgid "Edit Any Metadata" msgstr "Редактировать любые метаданные" #: f.widgets.cc:416 f.widgets.cc:541 msgid "Batch Rename Tags" msgstr "" #: f.widgets.cc:417 f.widgets.cc:542 msgid "Batch Add/Change Metadata" msgstr "" #: f.widgets.cc:420 f.widgets.cc:544 f.widgets.cc:618 msgid "Images by Geotag" msgstr "" #: f.widgets.cc:421 f.widgets.cc:545 f.widgets.cc:619 msgid "Search Images" msgstr "Поиск изображений" #: f.widgets.cc:424 fotoxx.h:1168 msgid "Select" msgstr "Выбрать" #: f.widgets.cc:425 fotoxx.h:1170 msgid "Show" msgstr "Показать" #: f.widgets.cc:426 fotoxx.h:1124 msgid "Hide" msgstr "Скрыть" #: f.widgets.cc:427 fotoxx.h:1110 msgid "Enable" msgstr "Включить" #: f.widgets.cc:428 fotoxx.h:1106 msgid "Disable" msgstr "Отключить" #: f.widgets.cc:429 fotoxx.h:1127 msgid "Invert" msgstr "Инвертировать" #: f.widgets.cc:430 fotoxx.h:1184 msgid "Unselect" msgstr "Отменить выбор" #: f.widgets.cc:431 msgid "Copy Area" msgstr "" #: f.widgets.cc:432 msgid "Paste Area" msgstr "" #: f.widgets.cc:433 msgid "Open Area File" msgstr "" #: f.widgets.cc:434 msgid "Save Area File" msgstr "" #: f.widgets.cc:439 msgid "Voodoo 1" msgstr "" #: f.widgets.cc:440 msgid "Voodoo 2" msgstr "" #: f.widgets.cc:442 f.widgets.cc:687 msgid "Brightness Dist." msgstr "Распределение яркости" #: f.widgets.cc:443 f.widgets.cc:688 msgid "Zonal Flatten" msgstr "" #: f.widgets.cc:447 msgid "Add Text" msgstr "Вписать текст" #: f.widgets.cc:448 msgid "Add Lines" msgstr "Добавить Линии" #: f.widgets.cc:451 msgid "Plugins" msgstr "Плагины" #: f.widgets.cc:456 msgid "Denoise" msgstr "Уменьшить шум" #: f.widgets.cc:458 msgid "Red Eyes" msgstr "Красные глаза" #: f.widgets.cc:463 msgid "Adjust RGB/CMY" msgstr "" #: f.widgets.cc:464 msgid "Adjust HSL" msgstr "" #: f.widgets.cc:465 msgid "Brightness Ramp" msgstr "Спад яркости" #: f.widgets.cc:467 msgid "Match Colors" msgstr "Подбор цветов" #: f.widgets.cc:468 msgid "Color Profile" msgstr "Профиль цветов" #: f.widgets.cc:470 msgid "Anti-Alias" msgstr "Антиалиасинг" #: f.widgets.cc:476 msgid "Fix Perspective" msgstr "Исправить Перспективу" #: f.widgets.cc:481 msgid "Flatten Book Page" msgstr "Выровнять страницу книги" #: f.widgets.cc:484 msgid "Color Depth" msgstr "Глубина цвета" #: f.widgets.cc:485 msgid "Sketch" msgstr "Набросок" #: f.widgets.cc:489 msgid "Embossing" msgstr "Рельефное тиснение" #: f.widgets.cc:491 msgid "Dots" msgstr "Точки" #: f.widgets.cc:492 msgid "Painting" msgstr "Живопись" #: f.widgets.cc:494 msgid "Texture" msgstr "Текстура" #: f.widgets.cc:496 msgid "Mosaic" msgstr "Мозаика" #: f.widgets.cc:503 msgid "High Dynamic Range" msgstr "Высокий динамический диапазон" #: f.widgets.cc:504 msgid "High Depth of Field" msgstr "Высокая глубина резкости" #: f.widgets.cc:505 msgid "Stack/Paint" msgstr "Стопка/Грим" #: f.widgets.cc:506 msgid "Stack/Noise" msgstr "Стопка/Шум" #: f.widgets.cc:507 msgid "Panorama" msgstr "Панорама" #: f.widgets.cc:508 msgid "Vertical Panorama" msgstr "Вертикальная панорама" #: f.widgets.cc:509 msgid "PT Panorama" msgstr "PT Панорама" #: f.widgets.cc:510 msgid "Mashup" msgstr "Коллаж" #: f.widgets.cc:515 msgid "Keyboard Shortcuts" msgstr "Сочетания клавиш" #: f.widgets.cc:516 msgid "Show Brightness Dist." msgstr "Укажите Яркость" #: f.widgets.cc:521 msgid "Dark/Bright Pixels" msgstr "Тёмные/Яркие Пиксели" #: f.widgets.cc:522 msgid "Monitor Color" msgstr "Цветность монитора" #: f.widgets.cc:524 msgid "Change Language" msgstr "Изменить язык" #: f.widgets.cc:525 msgid "Missing Translations" msgstr "Пропущен перевод" #: f.widgets.cc:527 msgid "Resources" msgstr "Ресурсы" #: f.widgets.cc:534 msgid "Batch RAW (DCraw)" msgstr "Пакетная RAW-конв (DCraw)" #: f.widgets.cc:535 msgid "Batch RAW (Raw Therapee)" msgstr "Пакетная RAW-конв (Raw Therapee)" #: f.widgets.cc:536 msgid "Script Files" msgstr "" #: f.widgets.cc:537 msgid "Burn Images to CD/DVD" msgstr "Записать изображения на CD/DVD" #: f.widgets.cc:551 msgid "Cycle Desktop Wallpaper" msgstr "" #: f.widgets.cc:568 f.widgets.cc:591 f.widgets.cc:613 msgid "Gallery" msgstr "Галерея" #: f.widgets.cc:569 f.widgets.cc:592 f.widgets.cc:614 msgid "World Maps" msgstr "Карты Мира" #: f.widgets.cc:571 msgid "Favorites" msgstr "Любимые" #: f.widgets.cc:573 fotoxx.h:1164 msgid "Save" msgstr "Сохранить" #: f.widgets.cc:574 msgid "Prev/Next" msgstr "Пред/След" #: f.widgets.cc:575 msgid "Metadata" msgstr "Метаданные" #: f.widgets.cc:576 msgid "Areas" msgstr "Области" #: f.widgets.cc:577 fotoxx.h:1109 msgid "Edit" msgstr "Редактировать" #: f.widgets.cc:578 msgid "Repair" msgstr "Исправить" #: f.widgets.cc:579 msgid "Bend" msgstr "Деформация" #: f.widgets.cc:580 msgid "Effects" msgstr "Эффекты" #: f.widgets.cc:581 msgid "Combine" msgstr "Объединение" #: f.widgets.cc:582 msgid "Undo/Redo" msgstr "Отменить/Повторить" #: f.widgets.cc:583 msgid "Tools" msgstr "Инструменты" #: f.widgets.cc:594 #, fuzzy msgid "Sync.G" msgstr "Галерея" #: f.widgets.cc:595 msgid "Albums" msgstr "" #: f.widgets.cc:596 msgid "Bookmarks" msgstr "" #: f.widgets.cc:605 msgid "Batch" msgstr "" #: f.widgets.cc:616 msgid "Choose Map" msgstr "Выберите карту" #: f.widgets.cc:617 msgid "Search Range" msgstr "Радиус Поиска" #: f.widgets.cc:625 fotoxx.h:1182 msgid "Undo" msgstr "Отменить" #: f.widgets.cc:626 fotoxx.h:1155 msgid "Redo" msgstr "Вернуть" #: f.widgets.cc:667 msgid "Popup Image" msgstr "Выпадающее изображение" #: f.widgets.cc:668 msgid "Popup Image (add)" msgstr "Всплывающее изображение (добавить)" #: f.widgets.cc:672 fotoxx.h:1158 msgid "Rename" msgstr "Переименовать" #: f.widgets.cc:673 msgid "Copy to Location" msgstr "Копировать в папку" #: f.widgets.cc:674 msgid "Move to Location" msgstr "Переместить в папку" #: f.widgets.cc:675 msgid "Copy to Clipboard" msgstr "Копировать в буфер" #: f.widgets.cc:676 msgid "Remove from Album" msgstr "Удалить из альбома" #: f.widgets.cc:677 msgid "Cut to Image Cache" msgstr "Вырезать в кэш изображений" #: f.widgets.cc:678 msgid "Copy to Image Cache" msgstr "Скопировать в кэш изображений" #: f.widgets.cc:679 msgid "Paste Image Cache Here (clear)" msgstr "Поместить сюда кэш изображений с очисткой" #: f.widgets.cc:680 msgid "Paste Image Cache Here (keep)" msgstr "Поместить сюда кэш изображений с сохранением" #: f.widgets.cc:681 msgid "Paste Current Image File Here" msgstr "" #: f.widgets.cc:685 msgid "Voodoo Enhance" msgstr "Улучшение Voodoo" #: f.widgets.cc:690 msgid "Select Area" msgstr "Выберите область" #: f.widgets.cc:693 fotoxx.h:1178 msgid "Trash" msgstr "Корзина" #: f.widgets.cc:819 msgid "Image Locations" msgstr "Раположение Фото" #: fotoxx-15.11.cc:373 msgid "Please install missing programs:" msgstr "Установите отсутствующие программы:" #: fotoxx-15.11.cc:662 msgid "Kill active dialog?" msgstr "Отменить активный диалог?" #: fotoxx-15.11.cc:733 msgid "(reduced)" msgstr "(сокращено)" #: fotoxx-15.11.cc:734 msgid "area active" msgstr "область активна" #: fotoxx-15.11.cc:735 msgid "dialog open" msgstr "диалог открыт" #: fotoxx-15.11.cc:736 msgid "blocked" msgstr "" #: fotoxx-15.11.cc:786 msgid "edits" msgstr "редактирование" #: fotoxx-15.11.cc:2793 msgid "Exceed 50 anchor points" msgstr "Превышать 50 точек привязки" #: fotoxx-15.11.cc:3013 msgid "load curve from a file" msgstr "Загрузить кривую из файла" #: fotoxx-15.11.cc:3079 msgid "curve file is invalid" msgstr "" #: fotoxx-15.11.cc:3093 msgid "save curve to a file" msgstr "Сохранить кривую в файл" #: fotoxx-15.11.cc:3165 msgid "Too many edits, please save image" msgstr "Слишком много изменений, пожалуйста сохраните изображение" #: fotoxx-15.11.cc:3170 msgid "this function cannot be scripted" msgstr "" #: fotoxx-15.11.cc:3187 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Выбранная область не активна.\n" "Продолжить?" #: fotoxx-15.11.cc:3499 msgid "file data does not fit dialog" msgstr "данные не подходят к диалогу" #: fotoxx-15.11.cc:3509 msgid "Save settings to a file" msgstr "Сохранить установки в файл" #: fotoxx-15.11.cc:3825 msgid "This action will discard changes to current image" msgstr "Это действие отменит все изменения текущего изображения" #: fotoxx-15.11.cc:3826 msgid "prior function still active" msgstr "активна предыдущая функция" #: fotoxx-15.11.cc:3827 fotoxx.h:1128 msgid "Keep" msgstr "Оставить" #: fotoxx-15.11.cc:3828 msgid "Discard" msgstr "Сбросить" #: fotoxx.h:1080 msgid "Add" msgstr "Добавить" #: fotoxx.h:1081 msgid "Add All" msgstr "Добавить все" #: fotoxx.h:1083 msgid "Amount" msgstr "Размер" #: fotoxx.h:1084 msgid "Angle" msgstr "Угол" #: fotoxx.h:1085 msgid "Apply" msgstr "Применить" #: fotoxx.h:1086 msgid "Auto" msgstr "" #: fotoxx.h:1087 msgid "Black" msgstr "Черный" #: fotoxx.h:1088 msgid "Blend Width" msgstr "Ширина области смешивания" #: fotoxx.h:1090 zfuncs.cc:10098 msgid "bottom" msgstr "Низ" #: fotoxx.h:1092 msgid "Browse" msgstr "Обзор" #: fotoxx.h:1093 msgid "Cancel" msgstr "Отмена" #: fotoxx.h:1094 msgid "Center" msgstr "Центр" #: fotoxx.h:1095 msgid "Choose" msgstr "Выбор" #: fotoxx.h:1096 msgid "Clear" msgstr "Очистить" #: fotoxx.h:1097 msgid "Color" msgstr "Цвет" #: fotoxx.h:1098 msgid "continue" msgstr "" #: fotoxx.h:1100 msgid "Copy" msgstr "Копировать" #: fotoxx.h:1101 msgid "Create" msgstr "Создать" #: fotoxx.h:1102 msgid "Curve File:" msgstr "Файл кривой:" #: fotoxx.h:1103 msgid "Cut" msgstr "Вырезать" #: fotoxx.h:1104 msgid "Deband" msgstr "Уменьшить постеризацию" #: fotoxx.h:1105 msgid "Delete" msgstr "Удалить" #: fotoxx.h:1107 msgid "Done" msgstr "Готово" #: fotoxx.h:1108 msgid "edge" msgstr "Кромка" #: fotoxx.h:1111 msgid "Erase" msgstr "Стереть" #: fotoxx.h:1112 msgid "Fetch" msgstr "Получить" #: fotoxx.h:1113 msgid "output file already exists" msgstr "такой файл уже существует" #: fotoxx.h:1114 #, c-format msgid "%d files selected" msgstr "%d файлов выбрано" #: fotoxx.h:1115 msgid "Find" msgstr "Поиск" #: fotoxx.h:1116 msgid "Finish" msgstr "Финиш" #: fotoxx.h:1117 msgid "Flatten" msgstr "Выровнять" #: fotoxx.h:1118 msgid "Font" msgstr "Шрифт" #: fotoxx.h:1119 msgid "Geotags" msgstr "Геотеги" #: fotoxx.h:1121 msgid "Grid" msgstr "Сетка" #: fotoxx.h:1122 msgid "Height" msgstr "Высота" #: fotoxx.h:1125 msgid "Images" msgstr "Изображения" #: fotoxx.h:1126 msgid "Insert" msgstr "Вставить" #: fotoxx.h:1129 zfuncs.cc:10102 msgid "left" msgstr "влево" #: fotoxx.h:1130 msgid "limit" msgstr "Лимит" #: fotoxx.h:1131 msgid "Load" msgstr "Загрузить" #: fotoxx.h:1132 msgid "Make" msgstr "Создать" #: fotoxx.h:1134 msgid "Map" msgstr "Карта" #: fotoxx.h:1135 msgid "Max" msgstr "Макс" #: fotoxx.h:1136 msgid "Negative" msgstr "Негатив" #: fotoxx.h:1137 msgid "New" msgstr "Новый" #: fotoxx.h:1138 msgid "Next" msgstr "Следующий" #: fotoxx.h:1139 zfuncs.cc:9190 msgid "No" msgstr "Нет" #: fotoxx.h:1140 msgid "no images" msgstr "нет изображений" #: fotoxx.h:1142 msgid "no selection" msgstr "ничего не выбрано" #: fotoxx.h:1143 msgid "OK" msgstr "ОК" #: fotoxx.h:1145 msgid "Paste" msgstr "Вставить" #: fotoxx.h:1146 msgid "Pause" msgstr "Пауза" #: fotoxx.h:1147 msgid "Percent" msgstr "Процент" #: fotoxx.h:1149 msgid "Presets" msgstr "Предустановленные" #: fotoxx.h:1150 msgid "Prev" msgstr "Предыдущий" #: fotoxx.h:1151 msgid "Proceed" msgstr "Продолжить" #: fotoxx.h:1153 msgid "range" msgstr "Диапазон" #: fotoxx.h:1156 msgid "Reduce" msgstr "Уменьшить" #: fotoxx.h:1157 msgid "Remove" msgstr "Удалить" #: fotoxx.h:1159 msgid "Replace" msgstr "" #: fotoxx.h:1160 msgid "Reserved" msgstr "" #: fotoxx.h:1161 msgid "Reset" msgstr "Сбросить" #: fotoxx.h:1162 zfuncs.cc:10106 msgid "right" msgstr "вправо" #: fotoxx.h:1163 msgid "Rotate" msgstr "Повернуть" #: fotoxx.h:1165 msgid "Unknown file type, save as tiff/jpeg/png to edit" msgstr "Неизвестный тип файла, сохраните как tiff/jpeg/png для редактирования" #: fotoxx.h:1166 msgid "Search" msgstr "Поиск" #: fotoxx.h:1167 msgid "Seconds" msgstr "Cекунд" #: fotoxx.h:1171 msgid "Size" msgstr "Размер" #: fotoxx.h:1172 msgid "Start" msgstr "Старт" #: fotoxx.h:1173 msgid "Strength" msgstr "Сила" #: fotoxx.h:1174 msgid "Threshold" msgstr "Порог" #: fotoxx.h:1175 #, c-format msgid "exceed %d files" msgstr "Превышено %d файлов" #: fotoxx.h:1176 zfuncs.cc:10094 msgid "top" msgstr "верх" #: fotoxx.h:1177 msgid "Transparency" msgstr "Прозрачность" #: fotoxx.h:1179 msgid "Trim" msgstr "Обрезка" #: fotoxx.h:1180 msgid "Undo All" msgstr "Отменить все" #: fotoxx.h:1181 msgid "Undo Last" msgstr "Отменить последнее" #: fotoxx.h:1183 msgid "Unfinish" msgstr "Не финиш" #: fotoxx.h:1185 msgid "View" msgstr "Вид" #: fotoxx.h:1186 msgid "Web" msgstr "Веб" #: fotoxx.h:1187 msgid "White" msgstr "Белый" #: fotoxx.h:1188 msgid "Width" msgstr "Ширина" #: fotoxx.h:1189 msgid "x-offset" msgstr "x-смещение" #: fotoxx.h:1190 msgid "y-offset" msgstr "y-смещение" #: fotoxx.h:1191 zfuncs.cc:9190 msgid "Yes" msgstr "Да" #: zfuncs.cc:1601 #, c-format msgid "" "create directory? \n" " %s" msgstr "" "Создать каталог? \n" " %s" #: zfuncs.cc:4378 msgid "user guide not found" msgstr "Инструкция не найдена" #: zfuncs.cc:5196 #, c-format msgid "cannot open file %s" msgstr "Не удалось открыть файл %s" #: zfuncs.cc:5232 msgid "save screen to file" msgstr "Сохранить экран в файл" #: zfuncs.cc:9266 zfuncs.cc:9761 zfuncs.cc:10081 msgid "cancel" msgstr "Отмена" #: zfuncs.cc:9722 msgid "choose file" msgstr "выберите файл" #: zfuncs.cc:9727 msgid "choose files" msgstr "выберите файлы" #: zfuncs.cc:9732 msgid "save" msgstr "Сохранить" #: zfuncs.cc:9738 msgid "choose folder" msgstr "выберите каталог" #: zfuncs.cc:9743 msgid "choose folders" msgstr "выберите каталоги" #: zfuncs.cc:9748 msgid "create folder" msgstr "Создать каталог" #: zfuncs.cc:9755 msgid "hidden" msgstr "Скрытый" #: zfuncs.cc:10081 msgid "done" msgstr "Готово" #: zfuncs.cc:10081 zfuncs.cc:10090 msgid "margins" msgstr "Края" #: zfuncs.cc:10111 msgid "image scale" msgstr "Масштаб изображения" #: zfuncs.cc:10113 msgid "percent" msgstr "Процент" #: zfuncs.cc:10120 msgid "image" msgstr "изображение" #: zfuncs.cc:10124 msgid "width" msgstr "Ширина" #: zfuncs.cc:10128 msgid "height" msgstr "Высота" #~ msgid "Lock aspect ratio" #~ msgstr "Зафиксировать соотношение сторон" #~ msgid "Median Brightness" #~ msgstr "Усреднить по яркости" #~ msgid "Flatten Outliers 2" #~ msgstr "Усреднить выпадающие 2" #~ msgid "Flatten Outliers 1" #~ msgstr "Усреднить выпадающие 1" #~ msgid "Kuwahara method" #~ msgstr "Метод Кувахара" #~ msgid "brightness gradient" #~ msgstr "Градиент яркости" #~ msgid "unsharp mask" #~ msgstr "Нерезкая маска" #~ msgid "" #~ "new name/base/adder unreasonable\n" #~ " e.g. newname ### 100 10" #~ msgstr "" #~ "плохое новое имя/модификатор\n" #~ "например имя ### 100 10" #~ msgid "jpeg quality must be 1-100" #~ msgstr "Качество jpeg должно быть 1-100" #~ msgid "(%d images)" #~ msgstr "(%d изображений)" #~ msgid "(0 images)" #~ msgstr "(0 изображений)" fotoxx-15.11.1/locales/translate-de.po0000664000175000017500000032244512616075370016237 0ustar micomico# German translations for fotoxx package. # Copyright (C) 2015 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: 2015-11-01 13:54+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:78 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:80 msgid "Drag album thumbnail to new position." msgstr "Album-Thumbnail zu neuer Stelle ziehen." #: f.albums.cc:109 f.widgets.cc:548 msgid "Manage Albums" msgstr "Alben verwalten" #: f.albums.cc:120 f.albums.cc:258 msgid "Create or replace an album" msgstr "Album erstellen oder ersetzen" #: f.albums.cc:122 msgid "Album to view or edit" msgstr "Album zum Ansehen oder Bearbeiten" #: f.albums.cc:124 msgid "Select images, add to cache" msgstr "Bilder wählen, zu Cache addieren" #: f.albums.cc:126 msgid "Clear image cache" msgstr "Bildercache löschen" #: f.albums.cc:128 f.albums.cc:150 msgid "Delete an album" msgstr "Album löschen" #: f.albums.cc:149 msgid "Choose Album" msgstr "Album wählen" #: f.albums.cc:151 #, c-format msgid "Image cache has %d images" msgstr "Bildercache hat %d Bilder" #: f.albums.cc:152 msgid "cache added to empty album" msgstr "Cache wurde zu leerem Album zugefügt" #: f.albums.cc:207 #, c-format msgid "delete %s ?" msgstr "%s löschen?" #: f.albums.cc:242 #, c-format msgid "fill from image cache (%d images)" msgstr "Vom Bild-Cache auffüllen (%d Bilder)" #: f.albums.cc:260 f.albums.cc:297 msgid "Album Name" msgstr "Album-Name" #: f.albums.cc:266 msgid "make an initially empty album" msgstr "Ein anfänglich leeres Album erstellen" #: f.albums.cc:268 msgid "fill from current gallery" msgstr "Von der aktuellen Galerie auffüllen" #: f.albums.cc:311 msgid "enter an album name" msgstr "Album-Name eingeben" #: f.albums.cc:338 msgid "gallery is empty" msgstr "Galerie ist leer" #: f.albums.cc:365 msgid "new album created" msgstr "Neues Album wurde erstellt" #: f.albums.cc:1099 msgid "Press ESC to exit slide show" msgstr "ESC drücken um zu beenden" #: f.albums.cc:1101 msgid "instant" msgstr "Sofortig" #: f.albums.cc:1102 msgid "fade-in" msgstr "Einblenden" #: f.albums.cc:1103 msgid "roll-right" msgstr "Nach rechts rollend" #: f.albums.cc:1104 msgid "roll-down" msgstr "Nach unten rollend" #: f.albums.cc:1105 msgid "venetian" msgstr "Jalousie" #: f.albums.cc:1106 msgid "grate" msgstr "Gitter" #: f.albums.cc:1107 msgid "rectangle" msgstr "Rechteck" #: f.albums.cc:1108 msgid "implode" msgstr "Implodieren" #: f.albums.cc:1109 msgid "explode" msgstr "Explodieren" #: f.albums.cc:1110 msgid "radar" msgstr "Radar" #: f.albums.cc:1111 msgid "Japanese-fan" msgstr "Japanischer-Fächer" #: f.albums.cc:1112 msgid "jaws" msgstr "Haifisch" #: f.albums.cc:1113 msgid "ellipse" msgstr "Ellipse" #: f.albums.cc:1114 msgid "raindrops" msgstr "Regentropfen" #: f.albums.cc:1115 msgid "doubledoor" msgstr "Doppeltür" #: f.albums.cc:1116 msgid "rotate" msgstr "drehen" #: f.albums.cc:1117 msgid "fallover" msgstr "Umkippen" #: f.albums.cc:1118 msgid "sphereoid" msgstr "Sphäroid" #: f.albums.cc:1139 f.widgets.cc:549 msgid "Slide Show" msgstr "Dia-Show" #: f.albums.cc:1150 msgid "Clip Limit" msgstr "Kappenlimit" #: f.albums.cc:1154 msgid "Music File" msgstr "Musikdatei" #: f.albums.cc:1159 msgid "Full Screen" msgstr "Fenster maximieren" #: f.albums.cc:1160 msgid "Auto-replay" msgstr "Auto-Wiederholung" #: f.albums.cc:1166 msgid "Customize:" msgstr "Anpassen:" #: f.albums.cc:1167 msgid "transitions" msgstr "Übergänge" #: f.albums.cc:1168 msgid "image files" msgstr "Bilddateien" #: f.albums.cc:1173 f.albums.cc:1289 #, c-format msgid "%d images" msgstr "%d Bilder" #: f.albums.cc:1195 f.albums.cc:1259 f.albums.cc:1349 f.albums.cc:1462 #: f.albums.cc:1655 f.albums.cc:1739 f.albums.cc:1756 f.albums.cc:1977 msgid "invalid album" msgstr "Ungültiges Album" #: f.albums.cc:1273 msgid "open album" msgstr "Album öffnen" #: f.albums.cc:1305 msgid "Select music file" msgstr "Musikdatei auswählen" #: f.albums.cc:1329 msgid "select random (if 5+ enabled)" msgstr "Zufallsordnung (wenn 5+ freigegeben" #: f.albums.cc:1353 msgid "Transition Preferences" msgstr "Übergangs Präferenzen" #: f.albums.cc:1362 msgid "transition" msgstr "Übergang" #: f.albums.cc:1363 msgid "enabled" msgstr "Freigegeben" #: f.albums.cc:1364 msgid "slowdown" msgstr "Bremsung" #: f.albums.cc:1365 msgid "preference" msgstr "Präferenz" #: f.albums.cc:1468 msgid "Image Preferences" msgstr "Bild Präferenzen" #: f.albums.cc:1472 f.file.cc:1159 f.file.cc:1400 f.file.cc:1608 f.meta.cc:437 msgid "Image File:" msgstr "Bilddatei:" #: f.albums.cc:1476 msgid "Play tone when image shows" msgstr "Klang spielen wenn Bild erscheint" #: f.albums.cc:1479 msgid "Show image caption" msgstr "Bild-Titel zeigen" #: f.albums.cc:1484 msgid "Show image comments" msgstr "Bild-Kommentare zeigen" #: f.albums.cc:1489 msgid "Wait before zoom" msgstr "Warten vor Zoom" #: f.albums.cc:1494 msgid "Zoom type:" msgstr "Zoom Art" #: f.albums.cc:1495 msgid "none" msgstr "keine" #: f.albums.cc:1496 msgid "zoom-in" msgstr "zoom-hinein" #: f.albums.cc:1497 msgid "zoom-out" msgstr "zoom-heraus" #: f.albums.cc:1500 msgid "Zoom size (x)" msgstr "Zoomgröße" #: f.albums.cc:1503 msgid "Steps" msgstr "Schritte" #: f.albums.cc:1507 msgid "Zoom image location:" msgstr "Zoom Bild-Stelle" #: f.albums.cc:1515 msgid "Wait after zoom" msgstr "Warten nach Zoom" #: f.albums.cc:1520 msgid "Transition to next image" msgstr "Übergang zum nächsten Bild" #: f.albums.cc:1523 f.albums.cc:1625 msgid "next" msgstr "nächst" #: f.albums.cc:1962 f.albums.cc:2120 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "Dateiformat Fehler: \n" " %s" #: f.area.cc:79 msgid "Select Area for Edits" msgstr "Ausschnitt für Bearbeitung auswählen" #: f.area.cc:80 f.edit.cc:5927 msgid "Press F1 for help" msgstr "F1 für Hilfe drucken" #: f.area.cc:89 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Ausschnitt nicht unterstützt \n" "von dieser Bearbeitungsfunktion" #: f.area.cc:119 msgid "select rectangle" msgstr "Rechteck wählen" #: f.area.cc:120 msgid "select ellipse" msgstr "Ellipse wählen" #: f.area.cc:123 msgid "freehand draw" msgstr "Freihändig zeichnen" #: f.area.cc:124 msgid "follow edge" msgstr "Rand folgen" #: f.area.cc:125 msgid "draw/replace" msgstr "Zeichnen/ersetzen" #: f.area.cc:128 msgid "select area within mouse" msgstr "Ausschnitt innerhalb Maus selektieren" #: f.area.cc:131 msgid "select one matching color within mouse" msgstr "Eine übereinstimmende Farbe in Maus selektieren" #: f.area.cc:135 msgid "select all matching colors within mouse" msgstr "Alle übereinstimmenden Farben in Maus selektieren" #: f.area.cc:139 f.edit.cc:5972 msgid "mouse radius" msgstr "Mausradius" #: f.area.cc:142 msgid "match level %" msgstr "Farbübereinstimmung %" #: f.area.cc:147 msgid "search range" msgstr "Suchentfernung" #: f.area.cc:149 msgid "firewall" msgstr "Brandmauer" #: f.area.cc:152 msgid "Area Edge Blend Width" msgstr "Ausschnitt Rand Mischbreite" #: f.area.cc:155 msgid "Edge Creep" msgstr "Rand-Kriech" #: f.area.cc:160 msgid "Line Color:" msgstr "Linienfarbe:" #: f.area.cc:180 f.area.cc:181 msgid "area edits fade away within edge distance" msgstr "Ausschnitt-Änderungen klingen innerhalb Randweite ab" #: f.area.cc:360 f.area.cc:543 #, c-format msgid "exceed %d edits" msgstr "%d Edits überschritten" #: f.area.cc:1364 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:1392 msgid "finish area" msgstr "Ausschnitt fertigstellen" #: f.area.cc:1400 msgid "extend to corner:" msgstr "bis zur Ecke ausfahren:" #: f.area.cc:1441 msgid "searching" msgstr "wird gesucht" #: f.area.cc:1472 msgid "outline has a gap" msgstr "Umriss hat eine Lücke" #: f.area.cc:1476 msgid "success" msgstr "Erfolg" #: f.area.cc:1613 f.area.cc:1640 #, c-format msgid "found %d pixels" msgstr "%d Pixels gefunden" #: f.area.cc:1935 f.area.cc:1952 f.area.cc:1972 f.area.cc:2287 f.area.cc:2363 #: f.area.cc:2520 msgid "the area is not finished" msgstr "Der Ausschnitt ist nicht fertig" #: f.area.cc:2061 msgid "Edge calculation in progress" msgstr "Rand Berechnung in Arbeit" #: f.area.cc:2070 msgid "Area Edge Calc" msgstr "Ausschnitt Randberechnung" #: f.area.cc:2471 msgid "load area from a file" msgstr "Ausschnitt von Datei lesen" #: f.area.cc:2554 msgid "save area as a PNG file" msgstr "Ausschnitt als PNG Datei speichern" #: f.area.cc:2582 msgid "position with mouse click/drag" msgstr "Mit Maus klicken/ziehen positionieren" #: f.area.cc:2638 msgid "Paste Image" msgstr "Bild einfügen" #: f.area.cc:2643 f.batch.cc:1169 f.batch.cc:1660 msgid "resize" msgstr "Skalieren" #: f.batch.cc:107 f.widgets.cc:531 msgid "Batch Convert" msgstr "Bilder Stapel konvertieren" #: f.batch.cc:114 f.file.cc:942 msgid "New Name" msgstr "Neuer Name" #: f.batch.cc:118 msgid "Sequence Numbers" msgstr "Sequenznummern" #: f.batch.cc:120 msgid "base" msgstr "Basis" #: f.batch.cc:122 msgid "adder" msgstr "Zuwachs" #: f.batch.cc:127 msgid "New Location" msgstr "Neue Speicherstelle" #: f.batch.cc:132 msgid "New File Type" msgstr "Neue Dateityp" #: f.batch.cc:136 f.batch.cc:144 f.repair.cc:3861 msgid "no change" msgstr "Keine Änderung" #: f.batch.cc:139 msgid "max. Width" msgstr "Max. Breite" #: f.batch.cc:147 f.batch.cc:565 msgid "Delete Originals" msgstr "Originale löschen" #: f.batch.cc:148 f.batch.cc:562 msgid "Copy Metadata" msgstr "Metadaten kopieren" #: f.batch.cc:149 f.batch.cc:563 f.edit.cc:1413 f.widgets.cc:438 #: f.widgets.cc:684 msgid "Upright" msgstr "Aufrichten" #: f.batch.cc:152 f.batch.cc:564 f.batch.cc:1178 f.batch.cc:1669 #: f.repair.cc:118 f.widgets.cc:454 msgid "Sharpen" msgstr "Schärfen" #: f.batch.cc:154 f.batch.cc:1180 f.batch.cc:1671 msgid "amount" msgstr "Wert" #: f.batch.cc:157 f.batch.cc:1183 f.batch.cc:1674 msgid "threshold" msgstr "Schwelle" #: f.batch.cc:280 msgid "file type not supported" msgstr "Dateityp nicht unterstützt" #: f.batch.cc:344 msgid "cannot create new file" msgstr "Kann neue Datei nicht erstellen" #: f.batch.cc:391 msgid "updating albums ..." msgstr "Alben werden aktualisiert ..." #: f.batch.cc:464 f.batch.cc:1456 f.batch.cc:1862 f.file.cc:1208 msgid "Select directory" msgstr "Bildverzeichnis wählen" #: f.batch.cc:513 msgid "" "new name/base/adder unreasonable\n" " e.g. newname ### 100 10\n" " or ... [oldname] ..." msgstr "" "Neu Name/Basis/Zuwachs nicht sinnvoll\n" " e.g. NeuName ### 100 10\n" " oder ... [AltName] ..." #: f.batch.cc:537 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "Max. größe %d x %d nicht sinnvoll" #: f.batch.cc:556 #, c-format msgid "Convert %d image files" msgstr "%d Bilddatei konvertieren" #: f.batch.cc:557 msgid "Rename to" msgstr "Umbenennen zu" #: f.batch.cc:558 msgid "Convert to" msgstr "Konvertieren zu" #: f.batch.cc:559 msgid "Resize within" msgstr "Skalieren innerhalb" #: f.batch.cc:560 msgid "Output to" msgstr "Schreiben zu" #: f.batch.cc:566 msgid "PROCEED?" msgstr "FORTSETZEN?" #: f.batch.cc:720 f.widgets.cc:532 msgid "Batch Upright" msgstr "Stapel aufrichten" #: f.batch.cc:726 msgid "Survey all files" msgstr "Alle Dateien durchsuchen" #: f.batch.cc:780 msgid "file cannot be read" msgstr "Datei ist unlesbar" #: f.batch.cc:898 msgid "cannot select both options" msgstr "kann nicht beide Optionen auswählen" #: f.batch.cc:942 f.widgets.cc:533 msgid "Batch Delete/Trash" msgstr "Stapel Löschen/Papierkorb" #: f.batch.cc:947 msgid "delete" msgstr "löschen" #: f.batch.cc:950 msgid "trash" msgstr "Papierkorb" #: f.batch.cc:1112 msgid "Batch Convert RAW Files (DCraw)" msgstr "Stapel RAW konvertieren (DCraw)" #: f.batch.cc:1121 msgid "DCraw not installed" msgstr "DCraw nicht installiert" #: f.batch.cc:1156 f.batch.cc:1647 msgid "output location" msgstr "Ausgabe Speicherstelle" #: f.batch.cc:1161 f.batch.cc:1652 msgid "output file type" msgstr "Ausgabedateityp" #: f.batch.cc:1192 msgid "white balance" msgstr "Weiabgleich" #: f.batch.cc:1193 msgid "interpolation" msgstr "Interpolation" #: f.batch.cc:1194 msgid "color space" msgstr "Farbraum" #: f.batch.cc:1195 msgid "gamma curve" msgstr "Gamma-Kurve" #: f.batch.cc:1198 msgid "camera" msgstr "Kamera" #: f.batch.cc:1199 msgid "fixed" msgstr "Festgelegt" #: f.batch.cc:1200 msgid "calculated" msgstr "Berechnet" #: f.batch.cc:1217 msgid "default" msgstr "Vorgabe" #: f.batch.cc:1223 msgid "defaults" msgstr "Vorgaben" #: f.batch.cc:1610 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Stapel RAW konvertieren (Raw Therapee)" #: f.batch.cc:1619 f.file.cc:428 msgid "Raw Therapee not installed" msgstr "Raw Therapee nicht installiert" #: f.batch.cc:1959 msgid "start" msgstr "beginnen" #: f.batch.cc:1960 msgid "begin making a script file" msgstr "Skriptdatei neu beginnen" #: f.batch.cc:1962 msgid "close" msgstr "beenden" #: f.batch.cc:1963 msgid "finish making a script file" msgstr "Skriptdatei fertigstellen" #: f.batch.cc:1965 msgid "run" msgstr "laufen lassen" #: f.batch.cc:1966 msgid "execute a script file" msgstr "Skriptdatei laufen lassen" #: f.batch.cc:2020 msgid "script already started" msgstr "Skriptdatei schon angefangen" #: f.batch.cc:2024 msgid "open new script file" msgstr "Neue Skriptdatei öffnen" #: f.batch.cc:2056 msgid "script file error" msgstr "Skriptdatei Fehler" #: f.batch.cc:2061 #, c-format msgid "%s added to script" msgstr "%s zu Skript addiert" #: f.batch.cc:2074 msgid "no script file was started" msgstr "keine Skriptdatei ist im Aufbau" #: f.batch.cc:2082 msgid "script file closed" msgstr "Skriptdatei fertiggestellt" #: f.batch.cc:2101 msgid "script file is not closed" msgstr "Skriptdatei wurde nicht fertiggestellt" #: f.batch.cc:2105 msgid "select script file to run" msgstr "Skriptdatei zum starten wählen" #: f.batch.cc:2107 msgid "open script file" msgstr "Skriptdatei öffnen" #: f.batch.cc:2114 msgid "unknown script file" msgstr "unbekannte Skriptdatei" #: f.batch.cc:2121 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" "Skriptdatei: %s \n" " %s" #: f.batch.cc:2132 msgid "select image files to be processed" msgstr "Bilddatei zum bearbeiten auswählen" #: f.batch.cc:2144 f.batch.cc:2193 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "Öffnen Fehler: %s \n" " %s" #: f.batch.cc:2165 #, c-format msgid "unknown edit function: %s" msgstr "unbekannte Bearbeitungsfunktion: %s" #: f.batch.cc:2174 #, c-format msgid "load widgets failed: %s" msgstr "Widgets laden fehlgeschlagen: %s" #: f.batch.cc:2203 #, c-format msgid "script file format error: %s" msgstr "Skriptdatei Formatfehler: %s" #: f.batch.cc:2229 msgid "Brasero not installed" msgstr "Brasero nicht installiert" #: f.batch.cc:2318 f.widgets.cc:538 msgid "Find Duplicate Images" msgstr "Duplikat-Bilder suchen" #: f.batch.cc:2320 f.tools.cc:929 msgid "Thumbnail size" msgstr "Thumbnailgröße" #: f.batch.cc:2323 msgid "pixel difference" msgstr "Pixel-Differenz" #: f.batch.cc:2326 msgid "pixel count" msgstr "Pixel-Anzahl" #: f.batch.cc:2329 msgid "Images:" msgstr "Bilder:" #: f.batch.cc:2330 msgid "searching ..." msgstr "Suchen ..." #: f.batch.cc:2332 msgid "Duplicates:" msgstr "Duplikate:" #: f.batch.cc:2350 #, c-format msgid "only %d image thumbnails found" msgstr "Nur %d Bilder-Thumbnails gefunden" #: f.bend.cc:86 f.widgets.cc:475 msgid "Unbend" msgstr "Entkrümmen" #: f.bend.cc:94 f.edit.cc:4267 f.effects.cc:4725 msgid "vertical" msgstr "Vertikal" #: f.bend.cc:95 f.edit.cc:4266 f.effects.cc:4721 msgid "horizontal" msgstr "Horizontal" #: f.bend.cc:96 msgid "linear" msgstr "linear" #: f.bend.cc:99 msgid "curved" msgstr "kurvig" #: f.bend.cc:367 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.bend.cc:384 msgid "Perspective Correction" msgstr "Perspective Verbessern" #: f.bend.cc:590 msgid "must have 4 corners" msgstr "Vier Ecken sind nötig" #: f.bend.cc:712 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.bend.cc:725 f.widgets.cc:477 msgid "Warp area" msgstr "Ausschnitt Krümmen" #: f.bend.cc:730 msgid "start warp" msgstr "Krümmen starten" #: f.bend.cc:788 msgid "no active Select Area" msgstr "Ausschnitt nicht aktiviert" #: f.bend.cc:1129 f.bend.cc:1443 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.bend.cc:1147 f.widgets.cc:478 msgid "Warp curved" msgstr "Gebogen krümmen" #: f.bend.cc:1156 f.mashup.cc:1836 msgid "warp span" msgstr "Krümmen-Spannweite" #: f.bend.cc:1461 f.widgets.cc:479 msgid "Warp linear" msgstr "Linear krümmen" #: f.bend.cc:1776 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.bend.cc:1792 f.widgets.cc:480 msgid "Warp affine" msgstr "Affine krümmen" #: f.bend.cc:2138 msgid "Flatten Book Page Photo" msgstr "Foto von Buchseite flachmachen" #: f.bend.cc:2139 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.bend.cc:2142 msgid "Stretch curved-down surfaces:" msgstr "Nach unten gebogenen Flächen ausdehnen:" #: f.bend.cc:2197 msgid "top:" msgstr "Oben:" #: f.bend.cc:2200 msgid "bottom:" msgstr "Unten:" #: f.combine.cc:2108 f.combine.cc:2740 f.combine.cc:3396 f.combine.cc:3928 msgid "Select 2 to 9 files" msgstr "2-9 Dateien auswählen" #: f.combine.cc:2129 f.combine.cc:2761 f.combine.cc:3417 f.combine.cc:3949 msgid "Images are not all the same size" msgstr "Bilder sind nicht gleich gross" #: f.combine.cc:2473 msgid "Adjust Image Contributions" msgstr "Bild Beiträge einstellen" #: f.combine.cc:2477 msgid "dark pixels" msgstr "dunkele Pixel" #: f.combine.cc:2479 msgid "light pixels" msgstr "helle Pixel" #: f.combine.cc:2481 msgid "file:" msgstr "Datei:" #: f.combine.cc:2997 msgid "Paint and Warp Image" msgstr "Bild malen und krümmen" #: f.combine.cc:3001 f.combine.cc:3646 f.combine.cc:6237 f.effects.cc:3912 #: f.widgets.cc:567 f.widgets.cc:590 f.widgets.cc:612 msgid "Image" msgstr "Bild" #: f.combine.cc:3005 msgid "paint" msgstr "malen" #: f.combine.cc:3006 msgid "warp" msgstr "Krümmen" #: f.combine.cc:3644 msgid "Select and Paint Image" msgstr "Bild auswählen und malen" #: f.combine.cc:4120 msgid "Adjust Pixel Composition" msgstr "Pixel Zusammensetzung einstellen" #: f.combine.cc:4122 msgid "use average" msgstr "Durchschnitt benutzen" #: f.combine.cc:4123 msgid "use median" msgstr "Mittlewert benutzen" #: f.combine.cc:4125 msgid "omit low pixel" msgstr "Niedrigste Pixel weglassen" #: f.combine.cc:4126 msgid "omit high pixel" msgstr "Höchste Pixel weglassen" #: f.combine.cc:4388 f.combine.cc:5494 msgid "Select 2 to 4 files" msgstr "2-4 Dateien auswählen" #: f.combine.cc:4463 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:4465 f.combine.cc:5571 msgid "no curve (scanned image)" msgstr "Keine Kurve (gescanntes Bild)" #: f.combine.cc:4466 f.combine.cc:5572 msgid "Search for lens mm" msgstr "Suche für Lense Brennweite" #: f.combine.cc:4467 f.combine.cc:5573 msgid "Save lens mm → image EXIF" msgstr "Lens mm in Bild EXIF speichern" #: f.combine.cc:4528 f.combine.cc:5634 msgid "Pre-align Images" msgstr "Bilder grob anpassen" #: f.combine.cc:4532 f.combine.cc:5638 msgid "lens mm" msgstr "Objektiv-Brennweite mm" #: f.combine.cc:4537 f.combine.cc:5643 msgid "no auto warp" msgstr "Auto-Warp ausschalten" #: f.combine.cc:4539 f.combine.cc:5645 f.widgets.cc:445 f.widgets.cc:683 msgid "Resize" msgstr "Größe verändern" #: f.combine.cc:4540 f.combine.cc:5646 msgid "resize window" msgstr "Fenstergröße ändern" #: f.combine.cc:4548 f.combine.cc:5654 msgid "do not warp images during auto-alignment" msgstr "Bilder nicht während Rand-Anpassung krümmen" #: f.combine.cc:4593 f.combine.cc:5699 msgid "use two images only" msgstr "Zwei Bilder benutzen" #: f.combine.cc:4621 f.combine.cc:4825 f.combine.cc:5011 f.combine.cc:5725 #: f.combine.cc:5929 f.combine.cc:6115 msgid "Too little overlap, cannot align" msgstr "Unzureichende Überlappung, Ausrichtung nicht möglich" #: f.combine.cc:5089 f.combine.cc:6193 msgid "Match Brightness and Color" msgstr "Helligkeit und Farbe anpassen" #: f.combine.cc:5134 msgid "Select image" msgstr "Bild wählen" #: f.combine.cc:5149 f.combine.cc:6252 msgid "auto color" msgstr "Auto-Farbe" #: f.combine.cc:5150 f.combine.cc:6253 msgid "file color" msgstr "Datei-Farbe" #: f.combine.cc:5156 f.combine.cc:6259 msgid "mouse warp" msgstr "Maus verformen" #: f.combine.cc:5159 msgid "curved image" msgstr "Gekurvtes Bild" #: f.combine.cc:5569 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:6469 msgid "pano tools (hugin) not installed" msgstr "pano tools (hugin) nicht installiert" #: f.combine.cc:6478 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:173 f.widgets.cc:437 f.widgets.cc:682 msgid "Trim/Rotate" msgstr "Schneiden/Drehen" #: f.edit.cc:185 f.edit.cc:586 msgid "ratio" msgstr "Verhältnis" #: f.edit.cc:189 msgid "trim size:" msgstr "Schnittgröße" #: f.edit.cc:194 msgid "Lock Ratio" msgstr "Seitenverhältniss fixieren" #: f.edit.cc:199 msgid "Customize" msgstr "Anpassen" #: f.edit.cc:207 msgid "Rotate: degrees" msgstr "Drehen: Grad" #: f.edit.cc:209 msgid "auto-trim" msgstr "Autoschnitt" #: f.edit.cc:582 msgid "Trim Buttons" msgstr "Schnitt-Knöpfe" #: f.edit.cc:585 msgid "label" msgstr "Kennsatz" #: f.edit.cc:1411 msgid "Upright Image" msgstr "Bild aufrichten" #: f.edit.cc:1477 msgid "rotation unknown" msgstr "Drehrichtung unbekannt" #: f.edit.cc:2029 f.widgets.cc:441 f.widgets.cc:686 msgid "Retouch Combo" msgstr "Retusche Combo" #: f.edit.cc:2050 msgid "Amplifier" msgstr "Verstärker" #: f.edit.cc:2051 fotoxx.h:1091 msgid "Brightness" msgstr "Helligkeit" #: f.edit.cc:2052 f.effects.cc:3364 fotoxx.h:1099 msgid "Contrast" msgstr "Kontrast" #: f.edit.cc:2053 msgid "Low Color" msgstr "Niedrige Farbe" #: f.edit.cc:2054 msgid "Warmer" msgstr "Wärmer" #: f.edit.cc:2055 f.effects.cc:1011 msgid "Dark Areas" msgstr "Dunkle Bereiche" #: f.edit.cc:2064 msgid "Max." msgstr "Max." #: f.edit.cc:2065 f.edit.cc:2066 f.edit.cc:2067 msgid "High" msgstr "Hoch" #: f.edit.cc:2068 msgid "Cooler" msgstr "Kühler" #: f.edit.cc:2069 msgid "Bright" msgstr "Hell" #: f.edit.cc:2072 f.tools.cc:1631 msgid "Brightness Distribution" msgstr "Helligkeitsverteilung" #: f.edit.cc:2075 msgid "Click for white balance or black level" msgstr "Anklicken für Weißabgleich oder Schwarzwert" #: f.edit.cc:2078 msgid "Settings File" msgstr "Einstellungsdatei" #: f.edit.cc:2082 msgid "recall previous settings used" msgstr "Vorherige Einstellungen wiederverwenden" #: f.edit.cc:2726 msgid "Adjust Brightness Distribution" msgstr "Helligkeit-Verteilung Justieren" #: f.edit.cc:2765 msgid "Low Cutoff" msgstr "Niedrig Verschnitt" #: f.edit.cc:2766 msgid "High Cutoff" msgstr "Hoch Verschnitt" #: f.edit.cc:2767 msgid "Low Flatten" msgstr "Niedrig abflachen" #: f.edit.cc:2768 msgid "Mid Flatten" msgstr "Mittig abflachen" #: f.edit.cc:2769 msgid "High Flatten" msgstr "Hoch abflachen" #: f.edit.cc:2770 msgid "Low Stretch" msgstr "Niedrig strecken" #: f.edit.cc:2771 msgid "Mid Stretch" msgstr "Mittig strecken" #: f.edit.cc:2772 msgid "High Stretch" msgstr "Hoch strecken" #: f.edit.cc:3102 msgid "Zonal Flatten Brightness" msgstr "Zonal Ausgleichen" #: f.edit.cc:3135 msgid "Zones" msgstr "Zonen" #: f.edit.cc:3142 msgid "Deband Dark" msgstr "Entstreifen Dunkel" #: f.edit.cc:3145 msgid "Deband Bright" msgstr "Entstreifen Hell" #: f.edit.cc:3594 f.widgets.cc:444 f.widgets.cc:689 msgid "Tone Mapping" msgstr "Tone Mapping" #: f.edit.cc:3625 msgid "low" msgstr "niedrig" #: f.edit.cc:3627 msgid "high" msgstr "hoch" #: f.edit.cc:3630 msgid "Amplify" msgstr "Verstärken" #: f.edit.cc:4034 msgid "Resize Image" msgstr "Bildgröße verändern" #: f.edit.cc:4059 msgid "W/H Ratio:" msgstr "B/H Verhältnis:" #: f.edit.cc:4061 msgid "Lock" msgstr "Fixieren" #: f.edit.cc:4063 msgid "use previous settings" msgstr "vorherige Einstellungen benutzen" #: f.edit.cc:4262 f.widgets.cc:446 msgid "Flip" msgstr "Spiegeln" #: f.edit.cc:4368 msgid "+Version" msgstr "+Version" #: f.edit.cc:4391 msgid "Write Text on Image" msgstr "Texte auf Bild schreiben" #: f.edit.cc:4392 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:4441 f.mashup.cc:2437 msgid "Text" msgstr "Text" #: f.edit.cc:4446 msgid "Use metadata key" msgstr "Metadata Key benutzen" #: f.edit.cc:4451 msgid "Use text file" msgstr "Text-Datei benutzen" #: f.edit.cc:4470 f.mashup.cc:2455 msgid "text" msgstr "Text" #: f.edit.cc:4471 f.edit.cc:5309 f.mashup.cc:2456 f.mashup.cc:2798 msgid "backing" msgstr "Hinterfarbe" #: f.edit.cc:4472 f.edit.cc:5310 f.mashup.cc:2457 f.mashup.cc:2799 msgid "outline" msgstr "Umriss" #: f.edit.cc:4473 f.edit.cc:5311 f.mashup.cc:2458 f.mashup.cc:2800 msgid "shadow" msgstr "Shatten" #: f.edit.cc:4499 msgid "save to current file" msgstr "in aktueller Datei speichern" #: f.edit.cc:4500 f.file.cc:1909 msgid "save as new file version" msgstr "Als neuen Dateiversion speichern" #: f.edit.cc:4501 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:4662 f.mashup.cc:2557 f.tools.cc:1068 msgid "select font" msgstr "Font wählen" #: f.edit.cc:4926 msgid "text file is defective" msgstr "Textdatei ist defekt" #: f.edit.cc:5249 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:5280 msgid "Write Line or Arrow on Image" msgstr "Linie/Pfeil am Bild schreiben" #: f.edit.cc:5288 f.mashup.cc:2777 msgid "Line length" msgstr "Linien-Länge" #: f.edit.cc:5295 f.mashup.cc:2784 msgid "Arrow head" msgstr "Pfeil-Kopf" #: f.edit.cc:5308 f.mashup.cc:2797 msgid "line" msgstr "Linie" #: f.edit.cc:5337 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:5926 f.widgets.cc:449 msgid "Paint Edits" msgstr "Änderungen anmalen" #: f.edit.cc:5933 f.edit.cc:6162 fotoxx-15.11.cc:3179 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "Ausschnitt kann nicht behalten werden.\n" "Fortfahren?" #: f.edit.cc:5941 f.edit.cc:6170 f.tools.cc:2157 msgid "Edit function must be active" msgstr "Editfunktion muss aktiv sein" #: f.edit.cc:5946 msgid "Cannot use Paint Edits" msgstr "Änderungen Anmalen nicht erlaubt" #: f.edit.cc:5975 msgid "power: center" msgstr "Stärke: Mitte" #: f.edit.cc:5980 msgid "reset area" msgstr "zurücksetzen" #: f.edit.cc:6155 f.widgets.cc:450 msgid "Leverage Edits" msgstr "Änderungs-Hebelkraft" #: f.edit.cc:6156 msgid "Edit Function Amplifier" msgstr "Bearbeitungs-Verstärker" #: f.edit.cc:6175 msgid "Cannot use Leverage Edits" msgstr "Änderungs-Hebelkraft nicht erlaubt" #: f.edit.cc:6208 msgid "minimum" msgstr "Minimum" #: f.edit.cc:6210 msgid "maximum" msgstr "Maximum" #: f.edit.cc:6510 msgid "Edit Plugins" msgstr "Plugins bearbeiten" #: f.edit.cc:6511 msgid "Edit plugins menu" msgstr "Plugins-Menu editieren" #: f.edit.cc:6519 msgid "Run as Fotoxx edit function" msgstr "Als Fotoxx-Bearbeitungsfunktion benutzen" #: f.edit.cc:6757 msgid "Plugin working ..." msgstr "Plugin arbeitet ..." #: f.edit.cc:6766 msgid "plugin failed" msgstr "Plug-in fehlgeschlagen" #: f.effects.cc:64 msgid "Set color depth to 1-16 bits" msgstr "Farbtiefe auf 1-16 Bits festlegen" #: f.effects.cc:76 msgid "Set Color Depth" msgstr "Farbtiefe festlegen" #: f.effects.cc:220 msgid "Convert to Sketch" msgstr "In eine Zeichnung verwandeln" #: f.effects.cc:296 msgid "Clip Level" msgstr "Kappenwert" #: f.effects.cc:300 msgid "Algorithm" msgstr "Algorithmus" #: f.effects.cc:305 msgid "Foreground" msgstr "Vordergrund" #: f.effects.cc:308 msgid "Background" msgstr "Hintergrund" #: f.effects.cc:714 f.widgets.cc:486 msgid "Line Drawing" msgstr "Stift-Zeichnung" #: f.effects.cc:727 msgid "black/white" msgstr "schwarzweiß" #: f.effects.cc:1005 f.widgets.cc:487 msgid "Color Drawing" msgstr "Farb-Zeichnung" #: f.effects.cc:1012 msgid "Bright Areas" msgstr "Hellere Bereiche" #: f.effects.cc:1266 f.widgets.cc:488 msgid "Graduated Blur" msgstr "Gestaffelte Verwischen" #: f.effects.cc:1270 msgid "Contrast Limit" msgstr "Kontrast-Grenzwert" #: f.effects.cc:1273 f.repair.cc:775 msgid "Blur Radius" msgstr "Verwisch-Radius" #: f.effects.cc:1487 msgid "Simulate Embossing" msgstr "Prägen simulieren" #: f.effects.cc:1504 msgid "depth" msgstr "Tiefe" #: f.effects.cc:1706 msgid "Simulate Tiles" msgstr "Kacheln simulieren" #: f.effects.cc:1710 msgid "tile size" msgstr "Kachelgröße" #: f.effects.cc:1713 msgid "tile gap" msgstr "Spaltbreite" #: f.effects.cc:1716 msgid "3D depth" msgstr "3D Tiefe" #: f.effects.cc:1940 msgid "Convert Image to Dots" msgstr "Bild in Rasterpunkte umwandeln" #: f.effects.cc:1944 msgid "dot size" msgstr "Punktgröße" #: f.effects.cc:2169 msgid "Simulate Painting" msgstr "Malen Simulieren" #: f.effects.cc:2173 msgid "color depth" msgstr "Farbtiefe" #: f.effects.cc:2177 msgid "patch area goal" msgstr "Fleckgröße Ziel" #: f.effects.cc:2181 msgid "req. color match" msgstr "Farbanpassung" #: f.effects.cc:2185 msgid "borders" msgstr "Ränder" #: f.effects.cc:2758 f.widgets.cc:493 msgid "Vignette" msgstr "Vignette" #: f.effects.cc:3107 msgid "Add Texture" msgstr "Textur addieren" #: f.effects.cc:3324 msgid "Background Pattern" msgstr "Hintergrund Muster" #: f.effects.cc:3328 msgid "Pattern File:" msgstr "Musterdatei" #: f.effects.cc:3333 msgid "Geometry" msgstr "Abmaße" #: f.effects.cc:3334 msgid "Calculate" msgstr "Rechnen" #: f.effects.cc:3336 msgid "Zoom" msgstr "Zoom" #: f.effects.cc:3347 f.widgets.cc:495 msgid "Pattern" msgstr "Muster" #: f.effects.cc:3354 msgid "Overlap" msgstr "Überschneidung" #: f.effects.cc:3361 msgid "Opacity" msgstr "Opazität" #: f.effects.cc:3429 msgid "choose pattern tile" msgstr "Musterkachel wählen" #: f.effects.cc:3816 msgid "Create Mosaic" msgstr "Mosaik erstellen" #: f.effects.cc:3902 msgid "Tile" msgstr "Kachel" #: f.effects.cc:3910 f.widgets.cc:490 msgid "Tiles" msgstr "Kacheln" #: f.effects.cc:3916 msgid "Tile blending" msgstr "Kacheln einblenden" #: f.effects.cc:3996 #, c-format msgid "exceeded max. tiles: %d" msgstr "Max. Kacheln überschritten: %d" #: f.effects.cc:4011 #, c-format msgid "only %d tile images found" msgstr "Nur %d Kachelbilder gefunden" #: f.effects.cc:4404 f.widgets.cc:497 msgid "Custom Kernel" msgstr "Benutzerspezifischer Kernel" #: f.effects.cc:4408 msgid "Kernel size" msgstr "Kernelgröße" #: f.effects.cc:4411 msgid "Divisor" msgstr "Teiler" #: f.effects.cc:4414 msgid "Data file" msgstr "Kerneldatei" #: f.effects.cc:4450 fotoxx-15.11.cc:3440 msgid "Load settings from file" msgstr "Einstellungen von Datei laden" #: f.effects.cc:4711 f.widgets.cc:498 msgid "Make Waves" msgstr "Wellen machen" #: f.effects.cc:4718 msgid "wavelength" msgstr "Wellenlänge" #: f.effects.cc:4719 msgid "amplitude" msgstr "Amplitude" #: f.effects.cc:4720 msgid "variance" msgstr "Varianz" #: f.effects.cc:4731 msgid "perspective" msgstr "Perspektive" #: f.effects.cc:4905 msgid "Pull image using the mouse." msgstr "Bild mit der Maus schleppen." #: f.effects.cc:4926 f.widgets.cc:499 msgid "Directed Blur" msgstr "Gerichtete Verwischung" #: f.effects.cc:4930 msgid "blur span" msgstr "Verwischbreite" #: f.effects.cc:4933 msgid "intensity" msgstr "Stärke" #: f.effects.cc:5099 f.widgets.cc:500 msgid "Spherical Projection" msgstr "Sphärische Projektion" #: f.effects.cc:5143 msgid "Magnify" msgstr "Vergrößern" #: f.file.cc:186 msgid "" "use EXIF photo date or \n" " file modification date" msgstr "" "EXIF Fotodatum oder Datei \n" " Änderungsdatum benutzen" #: f.file.cc:203 f.widgets.cc:572 msgid "File" msgstr "Datei" #: f.file.cc:357 msgid "UFraw not installed" msgstr "UFraw nicht installiert" #: f.file.cc:373 f.widgets.cc:691 msgid "Open RAW file (ufraw)" msgstr "RAW-Datei öffnen (ufraw)" #: f.file.cc:378 f.file.cc:449 msgid "RAW type not registered in User Settings" msgstr "RAW-Typ nicht in Benutzereinstellungen registriert" #: f.file.cc:444 f.widgets.cc:692 msgid "Open RAW file (Raw Therapee)" msgstr "RAW-Datei öffnen (Raw Therapee)" #: f.file.cc:541 f.widgets.cc:395 msgid "Open Image File" msgstr "Bilddatei öffnen" #: f.file.cc:560 msgid "unknown file type" msgstr "Dateityp unbekannt" #: f.file.cc:753 msgid "Create Blank Image" msgstr "Leerbild erstellen" #: f.file.cc:755 msgid "file name" msgstr "Dateiname" #: f.file.cc:788 msgid "supply a file name" msgstr "Dateinamen eingeben" #: f.file.cc:934 f.widgets.cc:400 msgid "Rename Image File" msgstr "Bilddatei umbenennen" #: f.file.cc:941 msgid "Old Name" msgstr "Vorheriger Name" #: f.file.cc:951 msgid "previous name" msgstr "Vorheriger Name" #: f.file.cc:952 msgid "add 1" msgstr "1 addieren" #: f.file.cc:1120 msgid "Copy Image File" msgstr "Bilddatei kopieren" #: f.file.cc:1121 msgid "Move Image File" msgstr "Bilddatei versetzen" #: f.file.cc:1164 msgid "new location" msgstr "Neue Speicherstelle" #: f.file.cc:1236 msgid "new location is not a directory" msgstr "Neue Speicherstelle muß ein Verzeichnis sein" #: f.file.cc:1276 f.file.cc:1518 f.file.cc:1656 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "Löschen fehlgeschlagen: \n" " %s" #: f.file.cc:1363 f.widgets.cc:401 msgid "Trash Image File" msgstr "Bilddatei in den Abfall" #: f.file.cc:1364 f.file.cc:1571 msgid "(automatic step to next image)" msgstr "(automatisch zum nächsten Bild)" #: f.file.cc:1432 msgid "" "Linux standard trash does not work. \n" "Desktop trash folder will be created." msgstr "" "Linux Standard-Abfall funktioniert nicht. \n" "Desktop Abfall-Ordner wird erstellt." #: f.file.cc:1435 msgid "" "Linux and Desktop trash do not work. \n" "Permanently delete the image file?" msgstr "" "Linux und Desktop Abfall funktionieren nicht. \n" "Bilddatei permanent löschen?" #: f.file.cc:1438 #, c-format msgid "Cannot create trash folder: %s" msgstr "Kann Abfallkorb nicht erstellen: %s" #: f.file.cc:1450 msgid "Move read-only file to trash?" msgstr "Schreibgeschützte Datei in den Abfall?" #: f.file.cc:1505 #, c-format msgid "error: %s" msgstr "Fehler: %s" #: f.file.cc:1534 f.file.cc:1673 f.file.cc:2508 msgid "no more images" msgstr "keine Bilder mehr" #: f.file.cc:1570 msgid "Delete Image File - CANNOT BE REVERSED" msgstr "Bilddatei löschen - NICHT UMKEHRBAR" #: f.file.cc:1646 msgid "Delete read-only file?" msgstr "Schreibgeschützte Datei löschen?" #: f.file.cc:1891 msgid "Save Image File" msgstr "Bilddatei speichern" #: f.file.cc:1901 msgid "new version" msgstr "Neue Version" #: f.file.cc:1902 msgid "new file" msgstr "Neue Datei" #: f.file.cc:1903 msgid "replace file" msgstr "Datei ersetzen" #: f.file.cc:1910 f.file.cc:2281 msgid "save as new file name or type" msgstr "Als neuen Dateinamen oder Typ speichern" #: f.file.cc:1912 msgid "replace old file (OVERWRITE)" msgstr "Vorherige Datei ersetzen (ÜBERSCHREIBEN" #: f.file.cc:1950 f.file.cc:2057 msgid "cannot save as RAW type" msgstr "Kann nicht als RAW-Datei speichern" #: f.file.cc:2051 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:2191 msgid "Unable to copy EXIF/IPTC data" msgstr "Kann nicht EXIF/IPTC Daten kopieren" #: f.file.cc:2299 f.file.cc:2302 msgid "make current" msgstr "aktuell machen" #: f.file.cc:2300 msgid "(new file becomes current file)" msgstr "(neue Datei wird aktuelle Datei)" #: f.file.cc:2409 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Datei überschreiben? \n" " %s" #: f.file.cc:2555 f.widgets.cc:554 msgid "Quick Start" msgstr "Schnell-Start" #: f.file.cc:2558 f.widgets.cc:555 msgid "User Guide" msgstr "Benutzeranleitung" #: f.file.cc:2561 f.widgets.cc:556 msgid "User Guide Changes" msgstr "Benutzeranleitung Änderungen" #: f.file.cc:2564 f.widgets.cc:558 msgid "README" msgstr "LIESMICH" #: f.file.cc:2567 f.widgets.cc:557 msgid "Edit Functions Summary" msgstr "Verarbeitungsfunktionen Übersicht" #: f.file.cc:2570 f.widgets.cc:559 msgid "Change Log" msgstr "Änderungslog" #: f.file.cc:2573 f.widgets.cc:560 msgid "Log File" msgstr "Fehlerlogdatei" #: f.file.cc:2576 f.widgets.cc:561 msgid "Translations" msgstr "Übersetzungen" #: f.file.cc:2579 f.widgets.cc:562 msgid "Home Page" msgstr "Homepage" #: f.file.cc:2582 f.widgets.cc:563 msgid "About" msgstr "Über Fotoxx" #: f.file.cc:2586 f.widgets.cc:584 f.widgets.cc:606 fotoxx.h:1123 msgid "Help" msgstr "Hilfe" #: f.file.cc:2739 f.file.cc:2808 #, c-format msgid "file not found: %s" msgstr "Datei nicht gefunden: %s" #: f.file.cc:2745 f.file.cc:2814 #, c-format msgid "file type not supported: %s" msgstr "Dateityp nicht unterstützt: %s" #: f.gallery.cc:918 f.gallery.cc:1007 f.widgets.cc:604 msgid "Scroll" msgstr "Scrollen" #: f.gallery.cc:922 msgid "Sync" msgstr "Sync" #: f.gallery.cc:932 f.repair.cc:5845 f.repair.cc:5850 fotoxx.h:1144 msgid "Open" msgstr "Öffnen" #: f.gallery.cc:933 msgid "change directory" msgstr "Verzeichnis wechseln" #: f.gallery.cc:941 msgid "GoTo" msgstr "GehZu" #: f.gallery.cc:947 f.widgets.cc:599 msgid "Sort" msgstr "Sortieren" #: f.gallery.cc:953 f.gallery.cc:1517 f.gallery.cc:1518 f.gallery.cc:1520 #: f.widgets.cc:597 msgid "Zoom+" msgstr "Zoom+" #: f.gallery.cc:963 f.gallery.cc:1519 f.gallery.cc:1521 f.widgets.cc:598 msgid "Zoom-" msgstr "Zoom-" #: f.gallery.cc:975 f.gallery.cc:1525 msgid "Row↑" msgstr "Reihe↑" #: f.gallery.cc:983 f.gallery.cc:1526 msgid "Row↓" msgstr "Reihe↓" #: f.gallery.cc:991 f.gallery.cc:1523 f.gallery.cc:1530 f.widgets.cc:602 msgid "Page↑" msgstr "Seite↑" #: f.gallery.cc:999 f.gallery.cc:1524 f.gallery.cc:1531 f.widgets.cc:603 msgid "Page↓" msgstr "Seite↓" #: f.gallery.cc:1013 f.gallery.cc:1528 f.widgets.cc:600 msgid "First" msgstr "Erste" #: f.gallery.cc:1014 f.gallery.cc:1529 f.widgets.cc:601 msgid "Last" msgstr "Letzte" #: f.gallery.cc:1127 msgid "Choose image directory" msgstr "Bild-Verzeichnis wählen" #: f.gallery.cc:1134 f.gallery.cc:1148 msgid "recent" msgstr "kürzlich" #: f.gallery.cc:1135 f.gallery.cc:1153 msgid "newest" msgstr "neueste" #: f.gallery.cc:1217 msgid "no albums found" msgstr "Keine Alben gefunden" #: f.gallery.cc:1224 msgid "Choose album" msgstr "Album wählen" #: f.gallery.cc:1274 msgid "Gallery Sort" msgstr "Galerie-Sortierung" #: f.gallery.cc:1278 msgid "File Name" msgstr "Dateinamen" #: f.gallery.cc:1279 msgid "File Mod Date/Time" msgstr "Dateiänderungs-Datum/Zeit" #: f.gallery.cc:1280 msgid "Photo Date/Time (EXIF)" msgstr "Foto Datum/Zeit (EXIF)" #: f.gallery.cc:1282 msgid "ascending" msgstr "aufsteigend" #: f.gallery.cc:1283 msgid "descending" msgstr "absteigend" #: f.gallery.cc:2212 fotoxx.h:1169 msgid "Select Files" msgstr "Dateien auswählen" #: f.gallery.cc:2672 msgid "Click list position. Click thumbnail to add." msgstr "Listenposition anklicken. Thumbnail anklicken um zu addieren." #: f.gallery.cc:2698 f.gallery.cc:2946 msgid "Edit Bookmarks" msgstr "Lesezeichen bearbeiten" #: f.gallery.cc:2873 msgid "unable to save bookmarks file" msgstr "Kann Lesezeichen-Datei nicht speichern" #: f.gallery.cc:2946 msgid "Go To Bookmark" msgstr "Zum Lesezeichen springen" #: f.gallery.cc:3003 f.meta.cc:2844 f.repair.cc:7473 msgid "file not found" msgstr "Datei nicht gefunden" #: 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:241 msgid "choose layout file" msgstr "Lageplan-Datei wählen" #: 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:417 msgid "Edit Images" msgstr "Bilder bearbeiten" #: f.mashup.cc:418 f.mashup.cc:2432 msgid "Edit Text" msgstr "Texte bearbeiten" #: f.mashup.cc:419 msgid "Edit Line" msgstr "Linie bearbeiten" #: f.mashup.cc:420 msgid "Rescale" msgstr "Neu skalieren" #: f.mashup.cc:423 msgid "add or edit images" msgstr "Bilder zufügen oder bearbeiten" #: f.mashup.cc:424 msgid "add or edit text" msgstr "Texte zufügen oder bearbeiten" #: f.mashup.cc:425 msgid "add or edit lines/arrows" msgstr "Linien/Pfeile zufügen oder bearbeiten" #: f.mashup.cc:426 msgid "change project scale" msgstr "Projekt-Maßstab ändern" #: f.mashup.cc:427 msgid "project complete" msgstr "Projekt fertig" #: f.mashup.cc:428 msgid "cancel project" msgstr "Projekt verwerfen" #: f.mashup.cc:446 msgid "rescale project" msgstr "Projekt neu skalieren" #: f.mashup.cc:501 msgid "save Mashup output file" msgstr "Ausgabedatei speichern" #: f.mashup.cc:512 msgid "save Mashup project file?" msgstr "Projektdatei speichern?" #: f.mashup.cc:524 msgid "delete Mashup project file?" msgstr "Projektdatei löschen?" #: f.mashup.cc:583 msgid "Open Project" msgstr "Projekt öffnen" #: f.mashup.cc:612 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "Lageplan-Bilddatei fehlt: \n" " %s" #: f.mashup.cc:669 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "Overlay-Bilddatei fehlt: \n" " %s" #: f.mashup.cc:974 msgid "project file is defective" msgstr "Projektdatei ist defekt" #: f.mashup.cc:1004 msgid "Save Project" msgstr "Projekt speichern" #: f.mashup.cc:1145 msgid "layout exceeds 2 gigabytes" msgstr "Lageplan überschreitet 2 Gigabytes" #: f.mashup.cc:1219 msgid "Click image to select, drag image to move." msgstr "Bild anklicken zum Wählen, schleppen zum Versetzen." #: f.mashup.cc:1220 msgid "Make black margins transparent" msgstr "Schwarze Ränder transparent machen" #: f.mashup.cc:1259 msgid "Current image:" msgstr "Aktuelles Bild" #: f.mashup.cc:1263 msgid "Cycle through images:" msgstr "Aktuelle Bilder durchsuchen" #: f.mashup.cc:1270 msgid "Scale" msgstr "Maßstab" #: f.mashup.cc:1279 msgid "Stacking Order" msgstr "Stapelfolge" #: f.mashup.cc:1280 msgid "raise" msgstr "Heben" #: f.mashup.cc:1281 msgid "lower" msgstr "Senken" #: f.mashup.cc:1284 msgid "Base Transparency" msgstr "Grund-Transparenz" #: f.mashup.cc:1288 msgid "Var. Transparency" msgstr "Var. Transparenz" #: f.mashup.cc:1289 msgid "Paint" msgstr "Malen" #: f.mashup.cc:1292 msgid "Bend and fine-align" msgstr "Biegen und fein einstellen" #: f.mashup.cc:1293 msgid "Warp" msgstr "Verformen" #: f.mashup.cc:1302 msgid "Margins" msgstr "Ränder" #: f.mashup.cc:1303 msgid "Hard" msgstr "Fest" #: f.mashup.cc:1304 f.repair.cc:5441 msgid "Blend" msgstr "Beimischen" #: f.mashup.cc:1318 msgid "add images to layout" msgstr "Bilder im Lageplan einfügen" #: f.mashup.cc:1557 msgid "Paint Image Transparencies" msgstr "Bild-Transparenze malen" #: f.mashup.cc:1575 msgid "Gradual" msgstr "Stufenweise" #: f.mashup.cc:1580 fotoxx.h:1148 msgid "Power" msgstr "Stärke" #: f.mashup.cc:1814 msgid "Pull on the image with the mouse." msgstr "Bildstelle mit der Maus ziehen" #: f.mashup.cc:1830 msgid "Warp Image" msgstr "Bild verformen" #: f.mashup.cc:2263 #, c-format msgid "exceeded %d images" msgstr "%d Bilder überschritten" #: f.mashup.cc:2405 msgid "Enter text, [Add] to layout, edit properties." msgstr "Text eingeben, Layout [Hinzufügen], Attribute justieren." #: f.mashup.cc:2485 msgid "Text File:" msgstr "Textdatei:" #: f.mashup.cc:2489 msgid "add entered text to layout" msgstr "Eingegebenen Text im Lageplan einfügen" #: f.mashup.cc:2618 msgid "click position to add text" msgstr "Bildstelle für Text anklicken" #: f.mashup.cc:2624 #, c-format msgid "exceeded %d text entries" msgstr "%d Texte überschritten" #: f.mashup.cc:2747 msgid "Set line properties, [Add] to layout, edit." msgstr "Linie Attribute justieren, Layout [Hinzufügen], einstellen." #: f.mashup.cc:2771 msgid "Edit Line/Arrow" msgstr "Linie/Pfeil bearbeiten" #: f.mashup.cc:2826 msgid "add line/arrow to layout" msgstr "Linie/Pfeil im Lageplan einfügen" #: f.mashup.cc:2919 msgid "click position to add line" msgstr "Bildstelle für Linie anklicken" #: f.mashup.cc:2925 #, c-format msgid "exceeded %d line entries" msgstr "%d Linie überschritten" #: f.meta.cc:180 f.widgets.cc:669 msgid "View Metadata" msgstr "Metadata ansehen" #: f.meta.cc:430 f.meta.cc:1807 f.widgets.cc:412 f.widgets.cc:670 msgid "Edit Metadata" msgstr "Metadata bearbeiten" #: f.meta.cc:433 f.meta.cc:3819 msgid "save metadata to file" msgstr "Metadaten in Datei speichern" #: f.meta.cc:442 msgid "Image Date" msgstr "Bild Datum" #: f.meta.cc:445 msgid "Time" msgstr "Zeit" #: f.meta.cc:451 msgid "Rating (stars):" msgstr "Bewertung (Sterne):" #: f.meta.cc:463 msgid "Caption" msgstr "Titel" #: f.meta.cc:469 msgid "Comments" msgstr "Kommentare" #: f.meta.cc:477 msgid "Enter New Tag" msgstr "Neues Tag eingeben" #: f.meta.cc:484 f.meta.cc:5003 msgid "Matching Tags" msgstr "Passende Tags" #: f.meta.cc:492 msgid "Image Tags" msgstr "Bild Tags" #: f.meta.cc:498 msgid "Recent Tags" msgstr "Kürzliche Tags" #: f.meta.cc:507 f.meta.cc:2029 f.meta.cc:5010 msgid "Defined Tags Category" msgstr "Definierte Tags Gruppe" #: f.meta.cc:898 msgid "date format is YYYY-MM-DD" msgstr "Datumformat ist JJJJ-MM-TT" #: f.meta.cc:902 msgid "date is invalid" msgstr "Datum ist ungültig" #: f.meta.cc:936 msgid "time format is HH:MM [:SS]" msgstr "Zeitformat ist HH:MM [:SS]" #: f.meta.cc:940 msgid "time is invalid" msgstr "Zeit ist ungültig" #: f.meta.cc:1025 fotoxx.h:1133 msgid "Manage Tags" msgstr "Tags verwalten" #: f.meta.cc:1025 msgid "orphan tags" msgstr "verwaiste Tags" #: f.meta.cc:1029 msgid "category" msgstr "Gruppe" #: f.meta.cc:1032 msgid "tag" msgstr "Tag" #: f.meta.cc:1039 msgid "Defined Tags:" msgstr "Definierte Tags:" #: f.meta.cc:1811 f.meta.cc:2474 msgid "key name" msgstr "Schlüsselwort" #: f.meta.cc:1813 f.meta.cc:2475 msgid "key value" msgstr "Inhalt" #: f.meta.cc:1900 f.widgets.cc:414 msgid "Delete Metadata" msgstr "Metadata löschen" #: f.meta.cc:1902 fotoxx.h:1082 msgid "All" msgstr "Alles" #: f.meta.cc:1903 msgid "One Key:" msgstr "Ein Key:" #: f.meta.cc:2006 f.widgets.cc:415 f.widgets.cc:540 msgid "Batch Add/Remove Tags" msgstr "Tags zuweisen/entfernen Stapelbetrieb" #: f.meta.cc:2017 msgid "tags to add" msgstr "Tags zum Zuweisen" #: f.meta.cc:2018 msgid "tags to remove" msgstr "Tags zum Entfernen" #: f.meta.cc:2102 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " Zuviele Tags" #: f.meta.cc:2217 msgid "specify files and tags" msgstr "Tags und Dateien angeben" #: f.meta.cc:2277 msgid "tag names file" msgstr "Tag-Namen Datei" #: f.meta.cc:2349 #, 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:2461 msgid "Batch Metadata" msgstr "Metadata Stapelbetrieb" #: f.meta.cc:2468 msgid "Short List" msgstr "Kurzliste" #: f.meta.cc:2469 msgid "Full List" msgstr "Vollliste" #: f.meta.cc:2511 msgid "enter key names" msgstr "Key-Namen eingeben" #: f.meta.cc:2517 fotoxx.h:1141 msgid "no files selected" msgstr "Keine Dateien ausgewählt" #: f.meta.cc:2643 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:3276 f.meta.cc:4011 f.meta.cc:4277 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "Latitude/Longitude falsch: %s %s" #: f.meta.cc:3320 f.meta.cc:3682 f.meta.cc:4486 f.meta.cc:4894 msgid "-noindex in use, disabled" msgstr "-noindex im Einsatz, gesperrt" #: f.meta.cc:3336 msgid "choose map file" msgstr "Kartendatei wählen" #: f.meta.cc:3444 msgid "" "fotoxx-maps package not installed \n" "(see http://kornelix.com/packages and /tarballs)" msgstr "" "fotoxx-maps Paket nicht installiert \n" "(http://kornelix.com/packages und /tarballs)" #: f.meta.cc:3449 #, c-format msgid "map file %s is missing" msgstr "Kartendatei %s fehlt" #: f.meta.cc:3453 #, 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:3731 msgid "No matching images found" msgstr "Keine übereinstimmenden Bilder gefunden" #: f.meta.cc:3771 msgid "search range (km)" msgstr "Suchentfernung (km)" #: f.meta.cc:3804 f.widgets.cc:418 f.widgets.cc:671 msgid "Edit Geotags" msgstr "Geotags bearbeiten" #: f.meta.cc:3805 msgid "Geocoding web service courtesy of" msgstr "Geocodingdienst mit Genehmigung von" #: f.meta.cc:3821 f.meta.cc:4060 f.meta.cc:5783 msgid "city" msgstr "Stadt" #: f.meta.cc:3824 f.meta.cc:4063 f.meta.cc:5786 msgid "country" msgstr "Land" #: f.meta.cc:3906 f.meta.cc:4194 msgid "city not found" msgstr "Stadt nicht gefunden" #: f.meta.cc:4027 f.widgets.cc:419 f.widgets.cc:543 msgid "Batch Add Geotags" msgstr "Geotags zu Bilder addieren" #: f.meta.cc:4256 msgid "" "data is incomplete \n" " proceed?" msgstr "" "Daten nicht vollständig \n" " Fortsetzen?" #: f.meta.cc:4313 msgid "choose city" msgstr "Stadt auswählen" #: f.meta.cc:4400 msgid "not found" msgstr "Nicht gefunden" #: f.meta.cc:4401 msgid "city and country required" msgstr "Stadt und Land erforderlich" #: f.meta.cc:4501 msgid "Report Geotag Groups" msgstr "Geotag-Gruppen auflisten" #: f.meta.cc:4502 msgid "Group by country" msgstr "Nach Land gruppieren" #: f.meta.cc:4503 msgid "Group by country/city" msgstr "Nach Land/Stadt gruppieren" #: f.meta.cc:4504 msgid "Group by country/city/date" msgstr "Nach Land/Stadt/Datum gruppieren" #: f.meta.cc:4507 msgid "Combine within" msgstr "Kombinieren innerhalb" #: f.meta.cc:4509 msgid "days" msgstr "Tage" #: f.meta.cc:4625 msgid "geotag groups" msgstr "Geotaggruppen" #: f.meta.cc:4931 msgid "Search Image Metadata" msgstr "Bilder-Metadaten suchen" #: f.meta.cc:4935 msgid "images to search:" msgstr "Bilder durchsuchen:" #: f.meta.cc:4936 msgid "all" msgstr "Alle" #: f.meta.cc:4937 msgid "current set only" msgstr "nur Aktueller Satz" #: f.meta.cc:4940 msgid "matching images:" msgstr "passende Bilder:" #: f.meta.cc:4941 msgid "new set" msgstr "neuer Satz" #: f.meta.cc:4942 msgid "add to set" msgstr "dem Satz zufügen" #: f.meta.cc:4943 msgid "remove" msgstr "entfernen" #: f.meta.cc:4946 msgid "report type:" msgstr "Bericht-Typ:" #: f.meta.cc:4947 msgid "gallery" msgstr "Galerie" #: f.meta.cc:4948 fotoxx-15.11.cc:791 msgid "metadata" msgstr "Metadaten" #: f.meta.cc:4954 msgid "date range" msgstr "Datumsbereich" #: f.meta.cc:4955 msgid "stars range" msgstr "Sterne-Wertebereich" #: f.meta.cc:4956 msgid "search tags" msgstr "Such-Tags" #: f.meta.cc:4957 msgid "search text" msgstr "Text durchsuchen" #: f.meta.cc:4958 msgid "search files" msgstr "Dateien suchen" #: f.meta.cc:4963 msgid "(yyyymmdd)" msgstr "(jjjjmmtt)" #: f.meta.cc:4968 msgid "last version only" msgstr "Nur Neueste Version" #: f.meta.cc:4970 msgid "all/any" msgstr "alle/irgendeines" #: f.meta.cc:4989 msgid "other criteria" msgstr "Andere Auswahlkriterien" #: f.meta.cc:4993 msgid "other" msgstr "Andere" #: f.meta.cc:4999 msgid "Enter Search Tag" msgstr "Such-Tag eingeben" #: f.meta.cc:5294 #, c-format msgid "not a defined tag: %s" msgstr "kein definiertes Tag: %s" #: f.meta.cc:5325 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:5332 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:5376 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "Suchdaten sind sinnlos \n" " %s %s" #: f.meta.cc:5394 msgid "stars range not reasonable" msgstr "Stern-Spanne unvernünftig" #: f.meta.cc:5639 #, c-format msgid "images added: %d removed: %d new count: %d" msgstr "Bilder zugefügt: %d entfernt: %d neue Anzahl: %d" #: f.meta.cc:5642 msgid "no changes made" msgstr "keine Änderungen gemach" #: f.meta.cc:5780 msgid "Add Geotags Search Criteria" msgstr "Geotags Suchkriterien addieren" #: f.meta.cc:5799 msgid "range (km)" msgstr "Entfernung (km)" #: f.meta.cc:5892 msgid "error in latitude/longitude/range" msgstr "Fehler in Längengrad/Breitengrad/Entfernung" #: f.meta.cc:5956 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:5980 msgid "Additional Items for Report" msgstr "Zusätzliche Metadaten im Bericht" #: f.meta.cc:5986 msgid "Keyword" msgstr "Schlüsselwort" #: f.meta.cc:5993 msgid "Match Criteria" msgstr "Trefferkriterien" #: f.meta.cc:6553 msgid "image index is missing" msgstr "Bilddatei Index fehlt" #: f.repair.cc:1041 msgid "Apply repeatedly while watching the image." msgstr "Mehrmals anwenden während der Bildbeobachtung." #: f.repair.cc:1042 msgid "Measure" msgstr "Messen" #: f.repair.cc:1078 msgid "Noise Reduction" msgstr "Rauschverminderung" #: f.repair.cc:1099 msgid "Flatten 1" msgstr "Abflachen 1" #: f.repair.cc:1100 msgid "Flatten 2" msgstr "Abflachen 2" #: f.repair.cc:1101 msgid "Median" msgstr "Mittelwert" #: f.repair.cc:1121 msgid "dark areas" msgstr "Dunkle Bereiche" #: f.repair.cc:1123 msgid "all areas" msgstr "Alle Bereiche" #: f.repair.cc:1246 msgid "Measure Noise" msgstr "Rauschen messen" #: f.repair.cc:1247 msgid "Click on a monotone image area." msgstr "Gleichmäßigen Bildbereich anklicken." #: f.repair.cc:2177 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:2199 f.widgets.cc:457 msgid "Smart Erase" msgstr "Smart löschen" #: f.repair.cc:2204 fotoxx.h:1152 msgid "Radius" msgstr "Radius" #: f.repair.cc:2206 f.widgets.cc:455 msgid "Blur" msgstr "Verwischen" #: f.repair.cc:2209 msgid "New Area" msgstr "Neuer Ausschnitt" #: f.repair.cc:2558 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:2574 msgid "Red Eye Reduction" msgstr "Rote-Augen beseitigen" #: f.repair.cc:3038 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 "" "Shift + Links-Klick: Farbe oder Bild-Position wählen \n" "Links-Klick oder Ziehen: mit Farbe malen oder vom Bild kopieren \n" "Rechts-Klick oder Ziehen: Farbe oder Bild entfernen" #: f.repair.cc:3070 f.widgets.cc:459 msgid "Paint/Clone" msgstr "Malen/Klonen" #: f.repair.cc:3074 msgid "paint color" msgstr "Malfarbe" #: f.repair.cc:3077 msgid "copy from image" msgstr "Vom Bild kopieren" #: f.repair.cc:3084 f.repair.cc:3641 msgid "paintbrush radius" msgstr "Pinsel-Radius" #: f.repair.cc:3085 msgid "transparency center" msgstr "Transparenz Mitte" #: f.repair.cc:3086 msgid "transparency edge" msgstr "Transparenz Rand" #: f.repair.cc:3093 f.repair.cc:3648 msgid "gradual paint" msgstr "Stufenweise malen" #: f.repair.cc:3100 #, c-format msgid "Undo Memory %d%c" msgstr "Undo-Speicher %d%c" #: f.repair.cc:3436 msgid "" "Undo memory limit has been reached. \n" "Save work with [done], then resume painting." msgstr "" "Undo Speicherkapazität erreicht. \n" "Arbeit mit [fertig] sichern, dann Fortfahren." #: f.repair.cc:3601 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "links ziehen: Transparenz addieren \n" "rechts ziehen: Opazität addieren" #: f.repair.cc:3633 f.widgets.cc:460 msgid "Paint Transparency" msgstr "Transparenz malen" #: f.repair.cc:3642 msgid "strength center" msgstr "Stärke Mitte" #: f.repair.cc:3643 msgid "strength edge" msgstr "Stärke Rand" #: f.repair.cc:3858 f.widgets.cc:461 msgid "Color Mode" msgstr "Farb-Modus" #: f.repair.cc:3862 msgid "black/white positive" msgstr "schwarzweiß Positiv" #: f.repair.cc:3863 msgid "black/white negative" msgstr "schwarzweiß Negativ" #: f.repair.cc:3864 msgid "color positive" msgstr "farbiges Positiv" #: f.repair.cc:3865 msgid "color negative" msgstr "farbiges Negativ" #: f.repair.cc:3866 msgid "sepia" msgstr "Sepia" #: f.repair.cc:4046 f.widgets.cc:462 msgid "Shift Colors" msgstr "Farben verschieben" #: f.repair.cc:4317 msgid "+Brightness" msgstr "+Helligkeit" #: f.repair.cc:4318 msgid "+Red -Cyan" msgstr "+Rot -Zyan" #: f.repair.cc:4319 msgid "+Green -Magenta" msgstr "+Grün -Magenta" #: f.repair.cc:4320 msgid "+Blue -Yellow" msgstr "+Blau -Gelb" #: f.repair.cc:4326 fotoxx.h:1154 msgid "Red" msgstr "Rot" #: f.repair.cc:4327 fotoxx.h:1120 msgid "Green" msgstr "Grün" #: f.repair.cc:4328 fotoxx.h:1089 msgid "Blue" msgstr "Blau" #: f.repair.cc:4623 msgid "Color to change" msgstr "zu ändernde Farbe" #: f.repair.cc:4625 msgid "shift+click on image to select color" msgstr "Shift+Klick am Bild zum Farbe wählen" #: f.repair.cc:4638 msgid "Color Hue" msgstr "Farbtöne" #: f.repair.cc:4639 msgid "Saturation" msgstr "Farbsättigung" #: f.repair.cc:4640 msgid "Lightness" msgstr "Helligkeit" #: f.repair.cc:4641 msgid "Color Match" msgstr "Farb-Übereinstimmung" #: f.repair.cc:4642 msgid "Adjustment" msgstr "Einstellung" #: f.repair.cc:5028 msgid "Ramp brightness across image" msgstr "Helligkeit an-/absteigend im Bild ändern" #: f.repair.cc:5352 f.tools.cc:2048 msgid "Click image to select pixels." msgstr "Bild anklicken zum Pixeln auswählen" #: f.repair.cc:5383 f.widgets.cc:466 msgid "Color Ramp" msgstr "Farbverlauf" #: f.repair.cc:5394 msgid "Metric:" msgstr "Einheit" #: f.repair.cc:5817 msgid "Color Match Images" msgstr "Bilderfarben anpassen" #: f.repair.cc:5843 msgid "mouse radius for color sample" msgstr "Mausradius für Farbauswahl" #: f.repair.cc:5846 msgid "image for source color" msgstr "Bild für Quellfarbe" #: f.repair.cc:5848 msgid "click on image to get source color" msgstr "Bild anklicken zur Quellfarbe bestimmen" #: f.repair.cc:5851 msgid "image to set matching color" msgstr "Bild zur Abgleichfarbe festlegen" #: f.repair.cc:5853 msgid "click on image to set matching color" msgstr "Bild anklicken zur Abgleichfarbe festlegen" #: f.repair.cc:5914 msgid "select source image color first" msgstr "Farbe vom Quellbild zuerst wählen" #: f.repair.cc:6099 msgid "Change Color Profile" msgstr "Farbraum umwandeln" #: f.repair.cc:6103 msgid "input profile" msgstr "Input-Farbraum" #: f.repair.cc:6107 msgid "output profile" msgstr "Ausgabe-Farbraum" #: f.repair.cc:6123 msgid "color profile" msgstr "Farbraum" #: f.repair.cc:6169 f.repair.cc:6175 #, c-format msgid "unknown cms profile %s" msgstr "CMS Farbraum unbekannt %s" #: f.repair.cc:6307 f.widgets.cc:469 msgid "Remove Dust" msgstr "Staub entfernen" #: f.repair.cc:6311 msgid "spot size limit" msgstr "Fleckgrößen Limit" #: f.repair.cc:6314 msgid "max. brightness" msgstr "max. Helligkeit" #: f.repair.cc:6317 msgid "min. contrast" msgstr "Min. Kontrast" #: f.repair.cc:7098 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:7121 f.widgets.cc:471 msgid "Color Fringes" msgstr "Farbsaume" #: f.repair.cc:7320 f.widgets.cc:472 msgid "Stuck Pixels" msgstr "Feste Pixels" #: f.repair.cc:7326 msgid "pixel group" msgstr "Pixelgruppe" #: f.repair.cc:7334 f.repair.cc:7402 msgid "stuck pixels:" msgstr "Feste Pixels:" #: f.repair.cc:7430 msgid "Load Stuck Pixels" msgstr "Festpixel laden" #: f.repair.cc:7432 msgid "File:" msgstr "Datei:" #: f.repair.cc:7457 f.repair.cc:7514 msgid "Stuck Pixels file" msgstr "Festpixel Datei" #: f.repair.cc:7494 msgid "file format error" msgstr "Dateiformat Fehler" #: f.tools.cc:70 msgid "" "Select directories containing image files \n" "(subdirectories are included automatically)." msgstr "" "Verzeichnisse mit Bilddateien auswählen \n" "(Unterverzeichnisse automatisch einbezogen)." #: f.tools.cc:72 msgid "Select to add, click on X to delete." msgstr "Auswählen zum Zufügen, X anklicken zum Löschen." #: f.tools.cc:73 msgid "Select directory for thumbnails." msgstr "Verzeichnis für Thumbnails auswählen." #: f.tools.cc:74 msgid "" "Index function terminated. Fotoxx will close. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Indizieren abgebrochen. Fotoxx wird schließen. \n" "Indizieren ist für Such- und Karten-Functionen erforderlich \n" "und damit die Gallerieseiten annehmbar schnell sind." #: f.tools.cc:106 f.widgets.cc:513 msgid "Index Image Files" msgstr "Bilddateien Indizieren" #: f.tools.cc:250 msgid "Choose top image directories" msgstr "Oberstes Bild-Verzeichnis wählen" #: f.tools.cc:251 msgid "Choose thumbnail directory" msgstr "Thumbnails-Verzeichnis wählen" #: f.tools.cc:324 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:330 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" "Ungültiges Verzeichnis: \n" " %s \n" "Bitte entfernen." #: f.tools.cc:333 #, 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:336 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" "Duplikat Verzeichnis: \n" " %s \n" "Bitte entfernen." #: f.tools.cc:385 msgid "no thumbnails directory defined" msgstr "Thumbnails-Verzeichnis nicht definiert" #: f.tools.cc:735 msgid "COMPLETED" msgstr "FERTIG" #: f.tools.cc:776 msgid "Indexing is required for first-time startup." msgstr "Indizieren ist für erstmaligen Anlauf erforderlich." #: f.tools.cc:838 msgid "Recent Files Gallery" msgstr "Zuletzt gesehene Bilder" #: f.tools.cc:839 msgid "Newest Files Gallery" msgstr "Neueste Bilddateien" #: f.tools.cc:840 msgid "Previous Gallery" msgstr "Vorherige Galerie" #: f.tools.cc:841 msgid "Previous Image" msgstr "Vorheriges Bild" #: f.tools.cc:842 msgid "Blank Window" msgstr "Leeres Fenster" #: f.tools.cc:843 msgid "Directory" msgstr "Verzeichnis" #: f.tools.cc:844 msgid "Image File" msgstr "Bilddatei" #: f.tools.cc:887 f.widgets.cc:514 msgid "User Options" msgstr "Benutzer Optionen" #: f.tools.cc:890 msgid "Startup Display" msgstr "Anfangsanzeige" #: f.tools.cc:901 msgid "Menu Style" msgstr "Menu-Stil" #: f.tools.cc:902 msgid "Icons" msgstr "Icons" #: f.tools.cc:903 msgid "Icons + Text" msgstr "Icons + Texte" #: f.tools.cc:905 msgid "Icon size" msgstr "Icon-Größe" #: f.tools.cc:909 msgid "Dialog font and size" msgstr "Dialog Font und Größe" #: f.tools.cc:914 msgid "Image Pan/scroll:" msgstr "Bild schwenken/scrollen:" #: f.tools.cc:917 msgid "Zooms for 2x" msgstr "Zooms für 2x" #: f.tools.cc:926 msgid "JPEG save quality" msgstr "JPEG Qualitätsgrad" #: f.tools.cc:933 msgid "Curve node capture distance" msgstr "Erfassungsabstand zum Kurvenknoten" #: f.tools.cc:937 msgid "show hidden directories in gallery view" msgstr "Versteckten Ordner in Galerie-Ansicht zeigen" #: f.tools.cc:940 msgid "prev/next shows last file version only" msgstr "Vorig/Nächst zeigt nur neueste Version" #: f.tools.cc:943 msgid "RAW command" msgstr "RAW Befehl" #: f.tools.cc:947 msgid "RAW file types" msgstr "RAW Dateitypen" #: f.tools.cc:1029 msgid "Delete present thumbnails to make effective" msgstr "Aktuelle Thumbnails löschen um wirksam zu machen" #: f.tools.cc:1051 msgid "Select startup directory" msgstr "Anfangsverzeichnis wählen" #: f.tools.cc:1058 msgid "Select startup image file" msgstr "Anfangsbilddatei wählen" #: f.tools.cc:1096 msgid "startup directory is invalid" msgstr "Anfangsverzeichnes ungültig" #: f.tools.cc:1107 msgid "startup file is invalid" msgstr "Anfangsdatei ungültig" #: f.tools.cc:1247 msgid "Edit KB Shortcuts" msgstr "Schnelltasten bearbeiten" #: f.tools.cc:1252 msgid "shortcut key:" msgstr "Schnelltaste:" #: f.tools.cc:1253 msgid "(enter key)" msgstr "(Enter-Taste)" #: f.tools.cc:1379 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Reserviert, kann nicht verwendet werden" #: f.tools.cc:1533 msgid "unable to save KB-shortcuts file" msgstr "Kann Schnelltasten-Datei nicht speichern" #: f.tools.cc:1837 f.widgets.cc:517 msgid "Grid Lines" msgstr "Gitterlinien" #: f.tools.cc:1846 msgid "x-spacing" msgstr "x-Abstand" #: f.tools.cc:1847 msgid "x-count" msgstr "x-Anzahl" #: f.tools.cc:1848 msgid "x-enable" msgstr "x-aktivieren" #: f.tools.cc:1854 msgid "y-spacing" msgstr "y-Abstand" #: f.tools.cc:1855 msgid "y-count" msgstr "y-Anzahl" #: f.tools.cc:1856 msgid "y-enable" msgstr "y-aktivieren" #: f.tools.cc:1976 f.tools.cc:1981 f.widgets.cc:518 msgid "Line Color" msgstr "Linienfarbe" #: f.tools.cc:1978 msgid "Area Color" msgstr "Ausschnittfarbe" #: f.tools.cc:2083 f.widgets.cc:519 msgid "Show RGB" msgstr "RGB-Werte zeigen" #: f.tools.cc:2375 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key M to toggle dialog." msgstr "" "Maus über Bild ziehen. \n" "Links klicken zum Abbrechen. \n" "Taste M zum Dialog starten/enden." #: f.tools.cc:2401 f.widgets.cc:520 msgid "Magnify Image" msgstr "Bild Vergrößern" #: f.tools.cc:2410 msgid "X-size" msgstr "X-Größe" #: f.tools.cc:2655 msgid "Darkest and Brightest Pixels" msgstr "Dunkelste und hellste Pixeln" #: f.tools.cc:2678 msgid "Dark Limit" msgstr "Dunkel Grenzwert" #: f.tools.cc:2679 msgid "Bright Limit" msgstr "Hell Grenzwert" #: f.tools.cc:2774 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:2870 f.widgets.cc:523 msgid "Monitor Gamma" msgstr "Bildschirm-Gamma" #: f.tools.cc:2930 msgid "Available Translations" msgstr "Vorhandene Übersetzungen" #: f.tools.cc:2934 msgid "Set Language" msgstr "Sprache wechseln" #: f.tools.cc:3071 f.widgets.cc:526 msgid "Calibrate Printer" msgstr "Drucker calibrieren" #: f.tools.cc:3096 msgid "print color chart" msgstr "Farbkarte drucken" #: f.tools.cc:3097 msgid "scan and save color chart" msgstr "Farbkarte einscannen und speichern" #: f.tools.cc:3098 msgid "align and trim color chart" msgstr "Farbkarte richten und schneiden" #: f.tools.cc:3099 msgid "open and process color chart" msgstr "Farbkarte öffnen und verarbeiten" #: f.tools.cc:3100 msgid "print image with revised colors" msgstr "Bilddatei mit geänderten Farben drucken" #: f.tools.cc:3264 #, c-format msgid "" "Scan the printed color chart. \n" "Save in %s/" msgstr "" "Gedruckte Farbkarte einscannen. \n" "Speichern in %s" #: f.tools.cc:3278 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation caused by the scanner. \n" "Trim off all margins. Be careful not to cut off \n" "any color tile edges or leave any white margins." msgstr "" "Eingescannte Farbkartedatei öffnen und bearbeiten. \n" "Schräglauf oder Verdrehung (vom Scanner) korrigieren. \n" "Ränder abschneiden. Seien Sie vorsichtig, die Farbkachel \n" "nicht abzuschneiden oder Ränder übrig zu lassen." #: f.tools.cc:3310 msgid "Open the trimmed color chart file" msgstr "Geschnittene Farbkartendatei öffnen" #: f.tools.cc:3443 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:3483 msgid "Color map file to use" msgstr "Farbdatendatei zur Verwendung" #: f.tools.cc:3500 msgid "Select the image file to print." msgstr "Bilddatei zum drucken öffnen." #: f.tools.cc:3565 msgid "converting colors..." msgstr "Farben werden konvertiert ..." #: f.tools.cc:3665 msgid "Image colors are converted for printing." msgstr "Bildfarben für das Drucken sind geändert." #: f.widgets.cc:111 msgid "Album" msgstr "Album" #: f.widgets.cc:113 msgid "TOP" msgstr "TOP" #: f.widgets.cc:165 msgid "Current Image File (key F)" msgstr "Aktuele Bilddatei (Taste F)" #: f.widgets.cc:166 msgid "Thumbnail Gallery (key G)" msgstr "Thumbnail-Galerie (Taste G)" #: f.widgets.cc:167 msgid "World Maps (key W)" msgstr "Weltkarten (Taste W)" #: f.widgets.cc:170 msgid "Favorite Functions" msgstr "Favoriten Funktionen" #: f.widgets.cc:171 msgid "File: Open, RAW, Rename, Trash, Print" msgstr "Datei: Öffnen, RAW, Umbenennen, Papierkorb, Drucken" #: f.widgets.cc:172 msgid "Save modified image file to disk" msgstr "Geänderte Bilddatei auf Festplatte speichern" #: f.widgets.cc:173 msgid "Open previous or next file (left/right mouse click)" msgstr "Vorherige oder Nächste Bilddatei öffnen (links/rechts Maus-Klick)" #: f.widgets.cc:174 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "Metadaten: Titeln, Tags, Bewertungen, Geotags, Suchen ..." #: f.widgets.cc:175 msgid "Areas: Select areas to edit, copy and paste" msgstr "Ausschnitt: Bildausschnitte wählen zum Bearbeiten, Kopieren, Einfügen" #: f.widgets.cc:176 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "" "Bearbeiten: Schneiden, Drehen, Skalieren, Helligkeit, Kontrast, Texte " "zufügen ..." #: f.widgets.cc:177 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "Ausbessern: Schärfen, Säubern, Rote-Augen, Farbe, Malen, Klonen ..." #: f.widgets.cc:178 msgid "Bend: Fix Perspective, Bend/Warp image ..." msgstr "Biegen: Perspektive verbessern, Bild entkrümmen ..." #: f.widgets.cc:179 msgid "Effects: Special Effects, Arty Transforms" msgstr "Effekte: Trickeffekte, kunstlerisch Verwandlungen" #: f.widgets.cc:180 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "Verbund: HDR, HDF, Panorama, Stack, Mashup" #: f.widgets.cc:181 msgid "" "Undo or Redo one edit (left/right mouse click) \n" " hold key A to include all edits" msgstr "" "Eine Änderung widerrufen oder zurückholen (links/rechts Maus-Klick) \n" " Taste A drucken, um alle Änderungen einzuschließen" #: f.widgets.cc:183 msgid "Tools: Index, Options, Shortcuts, Magnify ..." msgstr "Werkzeuge: Indizieren, Optionen, Abkürzungen, Vergrößern ..." #: f.widgets.cc:184 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "Hilfe: Schnellstart, Benutzeranweisung, neueste Änderungen ..." #: f.widgets.cc:187 msgid "Set gallery from current image file" msgstr "Galerie vom aktuellen Bild herbeiholen" #: f.widgets.cc:188 msgid "Albums: Manage Albums, Slide Show, Desktop Wallpaper" msgstr "Alben: Verwalten, Dia-Show, Bildschirmhintergrund" #: f.widgets.cc:189 msgid "go to bookmarked image" msgstr "Zum Lesezeichen-markierten Bild gehen" #: f.widgets.cc:190 msgid "increase thumbnail size" msgstr "Thumbnails vergrößern" #: f.widgets.cc:191 msgid "reduce thumbnail size" msgstr "Thumbnails verkleinern" #: f.widgets.cc:192 msgid "change sort order" msgstr "Reihenfolge ändern" #: f.widgets.cc:193 msgid "jump to beginning (top)" msgstr "Zum Anfang springen (oben)" #: f.widgets.cc:194 msgid "jump to end (bottom)" msgstr "Zu Ende springen (unten)" #: f.widgets.cc:195 msgid "previous page" msgstr "Vorherige Seite" #: f.widgets.cc:196 msgid "next page" msgstr "Nächste Seite" #: f.widgets.cc:197 msgid "slow scroll" msgstr "Langsam scrollen" #: f.widgets.cc:198 msgid "Batch conversion, metadata, RAW processing" msgstr "Stapel konvertieren, Metadaten, RAW Verarbeitung" #: f.widgets.cc:201 msgid "Choose a map for image locations" msgstr "Karte für Bilder-Ortsangabe wählen" #: f.widgets.cc:202 msgid "Set image search radius for map click" msgstr "Such-Radius von Karte-Anklick setzen" #: f.widgets.cc:205 msgid "Open another window" msgstr "Neues Bild-Fenster aufmachen" #: f.widgets.cc:206 msgid "Open a new image file" msgstr "Neue Bilddatei öffnen" #: f.widgets.cc:207 msgid "Open the previously seen file" msgstr "Die vorher gesehene Bilddatei öffnen" #: f.widgets.cc:208 msgid "Open a recently seen file" msgstr "Eine kürzliche gesehene Bilddatei öffnen" #: f.widgets.cc:209 msgid "Open a newly added file" msgstr "neu eingefügte Datei öffnen" #: f.widgets.cc:210 f.widgets.cc:211 msgid "Open and edit a camera RAW file" msgstr "RAW-Datei öffnen und bearbeiten" #: f.widgets.cc:212 msgid "Change the image file name" msgstr "Bilddateinamen ändern" #: f.widgets.cc:213 msgid "Create a blank image" msgstr "Leerbild erstellen" #: f.widgets.cc:214 msgid "Move image file to Trash" msgstr "Bilddatei in den Abfall schieben" #: f.widgets.cc:215 msgid "Permanently delete image file" msgstr "Bilddatei unwiederbringlich löschen" #: f.widgets.cc:216 msgid "Print the current image" msgstr "Aktuelles Bild drucken" #: f.widgets.cc:217 msgid "Print current image with adjusted colors" msgstr "Aktuelle Bilddatei mit angepassten Farben drucken" #: f.widgets.cc:218 f.widgets.cc:406 msgid "Quit Fotoxx" msgstr "Fotoxx beenden" #: f.widgets.cc:221 msgid "List a few key metadata items" msgstr "Wichtigste Metadaten auflisten" #: f.widgets.cc:222 msgid "List all metadata items" msgstr "Alle Metadaten auflisten" #: f.widgets.cc:223 msgid "(Toggle) show captions and comments" msgstr "(Knebel) Titel und Kommentare zeigen" #: f.widgets.cc:224 msgid "Edit image tags/caption/rating ..." msgstr "Bild Tags, Titel, Bewertung ... verarbeiten" #: f.widgets.cc:225 msgid "Edit any image metadata" msgstr "Beliebige Bild-Metadaten bearbeiten" #: f.widgets.cc:226 msgid "Remove all metadata from an image" msgstr "Alle Metadaten vom Bild entfernen" #: f.widgets.cc:227 msgid "Add/remove tags for multiple images" msgstr "Tags in mehreren Bilder zuweisen/entfernen" #: f.widgets.cc:228 msgid "Convert tag names for all images" msgstr "Tagnamen convertieren auf alle Bilder" #: f.widgets.cc:229 msgid "Add/change/delete metadata for multiple images" msgstr "Metadata addieren/ändern/löschen auf mehreren Bilder" #: f.widgets.cc:230 msgid "Edit image location and geotags" msgstr "Bild Ort und Geotag verarbeiten" #: f.widgets.cc:231 msgid "Add/revise geotags for multiple images" msgstr "Geotags addieren/ändern auf mehreren Bilder" #: f.widgets.cc:232 msgid "Find all images for a location [date]" msgstr "Alle Bilder für einen Ort [Datum] finden" #: f.widgets.cc:233 msgid "Find images meeting select criteria" msgstr "Bilder nach Auswahlkriterien finden" #: f.widgets.cc:236 msgid "Select object or area for editing" msgstr "Ausschnitt zum Bearbeiten auswählen" #: f.widgets.cc:237 msgid "Show (outline) existing area" msgstr "Aktuellen Ausschnitt zeigen (umranden)" #: f.widgets.cc:238 msgid "Hide existing area" msgstr "Aktuellen Ausschnitt ausblenden" #: f.widgets.cc:239 msgid "Enable area for editing" msgstr "Ausschnitt zum Bearbeiten aktivieren" #: f.widgets.cc:240 msgid "Disable area for editing" msgstr "Ausschnitt ausschalten" #: f.widgets.cc:241 msgid "Reverse existing area" msgstr "Aktuellen Ausschnitt ins Gegenteil verkehren" #: f.widgets.cc:242 msgid "Erase existing area" msgstr "Aktuellen Ausschnitt löschen" #: f.widgets.cc:243 msgid "Copy area for later pasting into image" msgstr "Ausschnitt für späteres Einfügen kopieren" #: f.widgets.cc:244 msgid "Paste previously copied area into image" msgstr "Vorher kopierten Ausschnitt ins Bild einfügen" #: f.widgets.cc:245 msgid "Open a file and paste as area into image" msgstr "Datei öffnen und als Auschnitt im Bild zufügen" #: f.widgets.cc:246 msgid "Save area to a file with transparency" msgstr "Auschnitt mit Transparenz in Datei speichern" #: f.widgets.cc:249 msgid "Trim/Crop margins and/or Rotate" msgstr "Ränder schneiden und/oder Drehen" #: f.widgets.cc:250 msgid "Upright a rotated image" msgstr "Gedrehtes Bild aufrichten" #: f.widgets.cc:251 f.widgets.cc:252 msgid "Fast auto enhance that may work OK" msgstr "Schnelle Auto-Verbesserung die klappen könnte" #: f.widgets.cc:253 msgid "Adjust brightness, contrast, color" msgstr "Helligkeit, Kontrast, Farbe einstellen" #: f.widgets.cc:254 msgid "Add local contrast, enhance details" msgstr "Lokalen Kontrast erhöhen, Details hervorheben" #: f.widgets.cc:255 msgid "Adjust brightness distribution" msgstr "Helligkeits-Verteilung justieren" #: f.widgets.cc:256 msgid "Flatten zonal brightness distribution" msgstr "Zonal Helligkeitsverteilung ausgleichen" #: f.widgets.cc:257 msgid "Change pixel dimensions" msgstr "Pixelgröße ändern" #: f.widgets.cc:258 msgid "Mirror image horizontally or vertically" msgstr "Bild horizontal oder vertikal spiegeln" #: f.widgets.cc:259 msgid "Write text on image" msgstr "Texte auf Bild schreiben" #: f.widgets.cc:260 msgid "Write lines or arrows on image" msgstr "Linien oder Pfeile am Bild schreiben" #: f.widgets.cc:261 msgid "Fix brightness uniformity across image" msgstr "Helligkeits-Ungleichheiten verbessern" #: f.widgets.cc:262 msgid "Paint edit function gradually with mouse" msgstr "Änderungs-Funktion allmählich mit der Maus anmalen" #: f.widgets.cc:263 msgid "Leverage edits by brightness or color" msgstr "Änderungen verstärken nach Helligkeit oder Farbwert" #: f.widgets.cc:264 msgid "Edit plugins menu or run a plugin function" msgstr "Plugins-Menu editieren oder Plugin-Funktion starten" #: f.widgets.cc:267 msgid "Make the image look sharper" msgstr "Bild schärfer machen" #: f.widgets.cc:268 msgid "Make the image look fuzzy" msgstr "Bild verschwommen machen" #: f.widgets.cc:269 msgid "Filter noise from low-light photos" msgstr "Rausch in unterbelichteten Fotos ausfiltern" #: f.widgets.cc:270 msgid "Remove unwanted objects" msgstr "Unerwünschte Gegenstände entfernen" #: f.widgets.cc:271 msgid "Fix red-eyes from electronic flash" msgstr "Rote Augen von Blitzfotos entfernen" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Bild-Pixeln mit der Maus anmalen" #: f.widgets.cc:273 msgid "Paint image transparency using the mouse" msgstr "Bild-Transparenz mit der Maus malen" #: f.widgets.cc:274 msgid "Make BW/color, negative/positive, sepia" msgstr "SW/farbiges Negativ/Positiv, Sepia machen" #: f.widgets.cc:275 msgid "Shift/convert colors into other colors" msgstr "Farben in andere Farben verwandeln" #: f.widgets.cc:276 msgid "Adjust color using RGB or CMY colors" msgstr "Farben justieren mit RGB oder CMY Farben" #: f.widgets.cc:277 msgid "Adjust color using HSL colors" msgstr "Farben justieren mit HSL Farben" #: f.widgets.cc:278 msgid "Adjust color in selected image areas" msgstr "Farben in ausgewählten Bereichen einstellen" #: f.widgets.cc:279 msgid "Match colors on one image with another" msgstr "Farben in einem Bild an ein anderes anpassen" #: f.widgets.cc:280 msgid "Convert to another color profile" msgstr "In einen anderen Farbraum umwandeln" #: f.widgets.cc:281 msgid "Remove dust spots from scanned slides" msgstr "Staubflecken von gescannten Dias entfernen" #: f.widgets.cc:282 msgid "Smoothen edges with jaggies" msgstr "Ränder mit Treppeneffekt glätten" #: f.widgets.cc:283 msgid "Reduce Chromatic Abberation" msgstr "Chromatische Aberration reduzieren" #: f.widgets.cc:284 msgid "Erase known hot and dark pixels" msgstr "Bekannte heiße und dunkle Pixeln löschen" #: f.widgets.cc:287 msgid "Remove curvature, esp. panoramas" msgstr "Entkrümmen, besonders bei Panoramen" #: f.widgets.cc:288 msgid "Straighten objects seen from an angle" msgstr "Aus einem Winkel gesehene Objekte gerade machen" #: f.widgets.cc:289 msgid "Distort image areas using the mouse" msgstr "Bild-Ausschnitt mit der Maus krümmen" #: f.widgets.cc:290 f.widgets.cc:291 f.widgets.cc:292 msgid "Distort the whole image using the mouse" msgstr "Ganzes Bild mit der Maus krümmen" #: f.widgets.cc:293 msgid "Flatten a photographed book page" msgstr "Foto von einer Buchseite flachmachen" #: f.widgets.cc:296 msgid "Reduce color depth (posterize)" msgstr "Farbtiefe reduzieren (Posterisation)" #: f.widgets.cc:297 msgid "Convert to pencil sketch" msgstr "In eine Stiftzeichnung verwandeln" #: f.widgets.cc:298 msgid "Convert to line drawing (edge detection)" msgstr "In eine Stift-Zeichnung verwandeln" #: f.widgets.cc:299 msgid "Convert to solid color drawing" msgstr "In eine Farb-Zeichnung verwandeln" #: f.widgets.cc:300 msgid "Graduated Blur depending on contrast" msgstr "Gestaffelte Verwischung je nach dem Kontrast" #: f.widgets.cc:301 msgid "Create an embossed or 3D appearance" msgstr "In eine Prägung mit 3D Wirkung verwandeln" #: f.widgets.cc:302 msgid "Convert to square tiles" msgstr "In Kacheln verwandeln" #: f.widgets.cc:303 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "In Rasterpunkte verwandeln (Roy Lichtenstein Effekt)" #: f.widgets.cc:304 msgid "Convert into a simulated painting" msgstr "In ein simuliertes Gemälde verwandeln" #: f.widgets.cc:305 msgid "Change brightness or color radially" msgstr "Helligkeit oder Farbwert radial ändern" #: f.widgets.cc:306 msgid "Add texture to an image" msgstr "Textur auf Bild zufügen" #: f.widgets.cc:307 msgid "Tile image with a repeating pattern" msgstr "Bild mit sich wiederholenden Muster fliesen" #: f.widgets.cc:308 msgid "Create a mosaic with tiles made from all images" msgstr "Mosaik mit Kacheln aus allen Bilder erstellen" #: f.widgets.cc:309 msgid "Process an image using a custom kernel" msgstr "Bild mit benutzerspezifischem Kernel bearbeiten" #: f.widgets.cc:310 msgid "Warp an image with a wave pattern" msgstr "Bild mit Wellenmuster verformen" #: f.widgets.cc:311 msgid "Blur an image in the direction of mouse drags" msgstr "Bild verwischen in Maus-Schlepp-Richtung" #: f.widgets.cc:312 msgid "Make a spherical projection of an image" msgstr "Sphärische Projektion vom Bild erzeugen" #: f.widgets.cc:315 msgid "Combine bright/dark images for better detail" msgstr "Hell- und Dunkel-Bilder kombinieren um Details zu verbessern" #: f.widgets.cc:316 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:317 msgid "Combine images to erase passing people, etc." msgstr "Bilder kombinieren um vorbeigehende Menschen, usw. zu entfernen" #: f.widgets.cc:318 msgid "Combine noisy images into a low-noise image" msgstr "Rauschige Bilder zu einem Niedrig-Rausch Bild kombinieren" #: f.widgets.cc:319 msgid "Combine images into a panorama" msgstr "Bilder zu einem Panorama-Bild kombinieren" #: f.widgets.cc:320 msgid "Combine images into a vertical panorama" msgstr "Bilder zu einem Vertikal-Panorama kombinieren" #: f.widgets.cc:321 msgid "Combine images into a panorama (panorama tools)" msgstr "Bilder zu einem Panorama-Bild kombinieren (panorama tools)" #: f.widgets.cc:322 msgid "Arrange images and text in a layout (montage)" msgstr "Bilder und Texte auf Lageplan ordnen (Montage)" #: f.widgets.cc:325 msgid "Index new files and make thumbnails" msgstr "Neue Dateien indizieren und Thumbnails erstellen" #: f.widgets.cc:326 msgid "Change user preferences" msgstr "Benutzereinstellungen ändern" #: f.widgets.cc:327 msgid "Change Keyboard Shortcut Keys" msgstr "Tastatur-Schnelltasten bearbeiten" #: f.widgets.cc:328 msgid "Show a brightness distribution graph" msgstr "Helligkeitsverteilungs-Grafik darstellen" #: f.widgets.cc:329 msgid "Show or revise grid lines" msgstr "Gitterlinien zeigen oder ändern" #: f.widgets.cc:330 msgid "Change color of foreground lines" msgstr "Vordergrund Linienfarbe ändern" #: f.widgets.cc:331 msgid "Show RGB colors at mouse click" msgstr "RGB-Werte auf Maus-klick zeigen" #: f.widgets.cc:332 msgid "Magnify image around the mouse position" msgstr "Bild um Maus-Position herum vergrößern" #: f.widgets.cc:333 msgid "Highlight darkest and brightest pixels" msgstr "Dunkelste und hellste Pixeln hervorheben" #: f.widgets.cc:334 msgid "Chart to adjust monitor color" msgstr "Grafik um Monitor-Farben einzustellen" #: f.widgets.cc:335 msgid "Chart to adjust monitor gamma" msgstr "Grafik um Monitor-Gamma einzustellen" #: f.widgets.cc:336 msgid "Change the GUI language" msgstr "GUI Sprache wechseln" #: f.widgets.cc:337 msgid "Report missing translations" msgstr "Fehlende Übersetzungen auflisten" #: f.widgets.cc:338 msgid "Calibrate printer colors" msgstr "Druckerfarben calibrieren" #: f.widgets.cc:339 msgid "Memory and CPU (to terminal/logfile)" msgstr "Speicher und CPU (zu Terminal/Logdatei)" #: f.widgets.cc:343 msgid "Rename/convert/resize/move multiple files" msgstr "Bilddateien umbenennen/konvertieren/skalieren/versetzen" #: f.widgets.cc:344 msgid "Upright multiple rotated image files" msgstr "Mehrere gedrehte Bilddateien aufrichten" #: f.widgets.cc:345 msgid "Delete or Trash multiple files" msgstr "Mehrere Dateien Löschen oder in Papierkorb" #: f.widgets.cc:346 msgid "Convert camera RAW files using DCraw" msgstr "Mehrere RAW-Dateien konvertieren mit DCraw" #: f.widgets.cc:347 msgid "Convert camera RAW files using Raw Therapee" msgstr "Mehrere RAW-Dateien konvertieren mit Raw Therapee" #: f.widgets.cc:348 msgid "Build and run edit script files" msgstr "Bearbeitungs-Skriptdatei bauen und laufen lassen" #: f.widgets.cc:349 msgid "Burn selected image files to CD or DVD" msgstr "Ausgewählte Bilddateien auf CD oder DVD brennen" #: f.widgets.cc:350 msgid "Search all image files and report duplicates" msgstr "Alle Bilddateien durchsuchen und Duplikate anzeigen" #: f.widgets.cc:354 msgid "Quick Start mini-guide" msgstr "Schnell-Start Mini-Anweisung" #: f.widgets.cc:355 msgid "Read the user guide" msgstr "Benutzeranleitung lesen" #: f.widgets.cc:356 msgid "Recent user guide changes" msgstr "Neueste Benutzeranleitungs-Änderungen" #: f.widgets.cc:357 msgid "Summary of image edit functions" msgstr "Bildbearbeitungsfunktionen Zusammenfassung" #: f.widgets.cc:358 msgid "Technical installation notes" msgstr "Technische Installierungsnotizen" #: f.widgets.cc:359 msgid "List updates by Fotoxx version" msgstr "Änderungen nach Fotoxxversion auflisten" #: f.widgets.cc:360 msgid "View the log file and error messages" msgstr "Logdatei und Fehlermeldungen auflisten" #: f.widgets.cc:361 msgid "How to do Fotoxx translations" msgstr "Wie eine Fotoxx Übersetzung erstellt wird" #: f.widgets.cc:362 msgid "Show the Fotoxx web page" msgstr "Fotoxx Webseite zeigen" #: f.widgets.cc:363 msgid "Version, license, contact, credits" msgstr "Version, Lizens, Kontakt, Mitwirkende" #: f.widgets.cc:366 msgid "Organize images into albums" msgstr "Bilder in Alben organisieren" #: f.widgets.cc:367 msgid "Start a slide show" msgstr "Dia-Show starten" #: f.widgets.cc:368 msgid "Set desktop wallpaper from current Fotoxx image" msgstr "Bildschirmhintergrund vom aktuellen Fotoxx Bild stellen" #: f.widgets.cc:369 msgid "Cycle desktop wallpaper from a Fotoxx album" msgstr "Hintergrundbilder wechseln von einem Fotoxx Album" #: f.widgets.cc:391 msgid "New Window" msgstr "Neues Fenster" #: f.widgets.cc:392 msgid "Sync Gallery" msgstr "Sync Galerie" #: f.widgets.cc:393 msgid "Recently Seen Images" msgstr "kürzlich gesehene Bilder" #: f.widgets.cc:394 msgid "Newest Images" msgstr "Neueste Bilder" #: f.widgets.cc:396 msgid "Open Previous File" msgstr "Vorherige Bilddatei öffnen" #: f.widgets.cc:397 msgid "Open RAW (ufraw)" msgstr "RAW öffnen (ufraw)" #: f.widgets.cc:398 msgid "Open RAW (Raw Therapee)" msgstr "RAW öffnen (Raw Therapee)" #: f.widgets.cc:399 msgid "New Blank Image" msgstr "Neues Leerbild" #: f.widgets.cc:402 msgid "Delete Image File" msgstr "Bilddatei löschen" #: f.widgets.cc:403 msgid "Print Image" msgstr "Bilddatei drucken" #: f.widgets.cc:404 msgid "Print Calibrated Image" msgstr "Kalibrierte Bilddatei drucken" #: f.widgets.cc:405 f.widgets.cc:550 msgid "Set Desktop Wallpaper" msgstr "Bildschirmhintergrund stellen" #: f.widgets.cc:409 msgid "View Metadata (short)" msgstr "Metadaten ansehen (kurz)" #: f.widgets.cc:410 msgid "View Metadata (long)" msgstr "Metadaten ansehen (lang)" #: f.widgets.cc:411 msgid "Show Captions on Image" msgstr "Titel am Bild zeigen" #: f.widgets.cc:413 msgid "Edit Any Metadata" msgstr "Beliebige Metadaten bearbeiten" #: f.widgets.cc:416 f.widgets.cc:541 msgid "Batch Rename Tags" msgstr "Stapel Tags umbenennen" #: f.widgets.cc:417 f.widgets.cc:542 msgid "Batch Add/Change Metadata" msgstr "Metadata addieren/ändern in Stapelfunktion" #: f.widgets.cc:420 f.widgets.cc:544 f.widgets.cc:618 msgid "Images by Geotag" msgstr "Bilder nach Geotag" #: f.widgets.cc:421 f.widgets.cc:545 f.widgets.cc:619 msgid "Search Images" msgstr "Bilddatei durchsuchen" #: f.widgets.cc:424 fotoxx.h:1168 msgid "Select" msgstr "Auswählen" #: f.widgets.cc:425 fotoxx.h:1170 msgid "Show" msgstr "Zeigen" #: f.widgets.cc:426 fotoxx.h:1124 msgid "Hide" msgstr "Ausblenden" #: f.widgets.cc:427 fotoxx.h:1110 msgid "Enable" msgstr "Aktivieren" #: f.widgets.cc:428 fotoxx.h:1106 msgid "Disable" msgstr "Ausschalten" #: f.widgets.cc:429 fotoxx.h:1127 msgid "Invert" msgstr "Invertieren" #: f.widgets.cc:430 fotoxx.h:1184 msgid "Unselect" msgstr "Deselektieren" #: f.widgets.cc:431 msgid "Copy Area" msgstr "Ausschnitt kopieren" #: f.widgets.cc:432 msgid "Paste Area" msgstr "Ausschnitt einfügen" #: f.widgets.cc:433 msgid "Open Area File" msgstr "Ausschnitt von Datei lesen" #: f.widgets.cc:434 msgid "Save Area File" msgstr "Ausschnitt in Datei speichern" #: f.widgets.cc:439 msgid "Voodoo 1" msgstr "Voodoo 1" #: f.widgets.cc:440 msgid "Voodoo 2" msgstr "Voodoo 2" #: f.widgets.cc:442 f.widgets.cc:687 msgid "Brightness Dist." msgstr "Helligkeitsverteilung" #: f.widgets.cc:443 f.widgets.cc:688 msgid "Zonal Flatten" msgstr "Zonal Ausgleichen" #: f.widgets.cc:447 msgid "Add Text" msgstr "Texte zufügen" #: f.widgets.cc:448 msgid "Add Lines" msgstr "Linien züfugen" #: f.widgets.cc:451 msgid "Plugins" msgstr "Plugins" #: f.widgets.cc:456 msgid "Denoise" msgstr "Entrauschen" #: f.widgets.cc:458 msgid "Red Eyes" msgstr "Rote Augen" #: f.widgets.cc:463 msgid "Adjust RGB/CMY" msgstr "RGB/CMY justieren" #: f.widgets.cc:464 msgid "Adjust HSL" msgstr "HSL justieren" #: f.widgets.cc:465 msgid "Brightness Ramp" msgstr "Helligkeit rampen" #: f.widgets.cc:467 msgid "Match Colors" msgstr "Farben anpassen" #: f.widgets.cc:468 msgid "Color Profile" msgstr "Farbraum" #: f.widgets.cc:470 msgid "Anti-Alias" msgstr "Kantenglättung" #: f.widgets.cc:476 msgid "Fix Perspective" msgstr "Perspective verbessern" #: f.widgets.cc:481 msgid "Flatten Book Page" msgstr "Buchseite flachmachen" #: f.widgets.cc:484 msgid "Color Depth" msgstr "Farbtiefe" #: f.widgets.cc:485 msgid "Sketch" msgstr "Skizzieren" #: f.widgets.cc:489 msgid "Embossing" msgstr "Prägung" #: f.widgets.cc:491 msgid "Dots" msgstr "Rasterpunkte" #: f.widgets.cc:492 msgid "Painting" msgstr "Gemälde" #: f.widgets.cc:494 msgid "Texture" msgstr "Textur" #: f.widgets.cc:496 msgid "Mosaic" msgstr "Mosaik" #: f.widgets.cc:503 msgid "High Dynamic Range" msgstr "Hoher Dynamikbereich" #: f.widgets.cc:504 msgid "High Depth of Field" msgstr "Hohe Schärfentiefe" #: f.widgets.cc:505 msgid "Stack/Paint" msgstr "Stapeln/malen" #: f.widgets.cc:506 msgid "Stack/Noise" msgstr "Stapeln/Rauschen" #: f.widgets.cc:507 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:508 msgid "Vertical Panorama" msgstr "Vertikales Panorama" #: f.widgets.cc:509 msgid "PT Panorama" msgstr "PT Panorama" #: f.widgets.cc:510 msgid "Mashup" msgstr "Mashup" #: f.widgets.cc:515 msgid "Keyboard Shortcuts" msgstr "Schnelltasten" #: f.widgets.cc:516 msgid "Show Brightness Dist." msgstr "Helligkeitsvert. zeigen" #: f.widgets.cc:521 msgid "Dark/Bright Pixels" msgstr "Dunkel/hell Pixeln" #: f.widgets.cc:522 msgid "Monitor Color" msgstr "Monitor Farbgrafik" #: f.widgets.cc:524 msgid "Change Language" msgstr "Sprache wechseln" #: f.widgets.cc:525 msgid "Missing Translations" msgstr "Fehlende Übersetzungen" #: f.widgets.cc:527 msgid "Resources" msgstr "Ressourcen" #: f.widgets.cc:534 msgid "Batch RAW (DCraw)" msgstr "Stapel RAW (DCraw)" #: f.widgets.cc:535 msgid "Batch RAW (Raw Therapee)" msgstr "Stapel RAW (Raw Therapee)" #: f.widgets.cc:536 msgid "Script Files" msgstr "Skriptdateien" #: f.widgets.cc:537 msgid "Burn Images to CD/DVD" msgstr "Bilder auf CD/DVD brennen" #: f.widgets.cc:551 msgid "Cycle Desktop Wallpaper" msgstr "Hintergrundbild Wechselzyklus" #: f.widgets.cc:568 f.widgets.cc:591 f.widgets.cc:613 msgid "Gallery" msgstr "Bildergalerie" #: f.widgets.cc:569 f.widgets.cc:592 f.widgets.cc:614 msgid "World Maps" msgstr "Weltkarten" #: f.widgets.cc:571 msgid "Favorites" msgstr "Favoriten" #: f.widgets.cc:573 fotoxx.h:1164 msgid "Save" msgstr "Speichern" #: f.widgets.cc:574 msgid "Prev/Next" msgstr "Vorig/Nächst" #: f.widgets.cc:575 msgid "Metadata" msgstr "Metadaten" #: f.widgets.cc:576 msgid "Areas" msgstr "Ausschnitt" #: f.widgets.cc:577 fotoxx.h:1109 msgid "Edit" msgstr "Bearbeiten" #: f.widgets.cc:578 msgid "Repair" msgstr "Ausbessern" #: f.widgets.cc:579 msgid "Bend" msgstr "Biegen" #: f.widgets.cc:580 msgid "Effects" msgstr "Effekte" #: f.widgets.cc:581 msgid "Combine" msgstr "Verbund" #: f.widgets.cc:582 msgid "Undo/Redo" msgstr "Undo/Redo" #: f.widgets.cc:583 msgid "Tools" msgstr "Werkzeuge" #: f.widgets.cc:594 msgid "Sync.G" msgstr "Sync.G" #: f.widgets.cc:595 msgid "Albums" msgstr "Alben" #: f.widgets.cc:596 msgid "Bookmarks" msgstr "Lesezeichnen" #: f.widgets.cc:605 msgid "Batch" msgstr "Stapel" #: f.widgets.cc:616 msgid "Choose Map" msgstr "Karte wählen" #: f.widgets.cc:617 msgid "Search Range" msgstr "Such-Umfang" #: f.widgets.cc:625 fotoxx.h:1182 msgid "Undo" msgstr "Undo" #: f.widgets.cc:626 fotoxx.h:1155 msgid "Redo" msgstr "Redo" #: f.widgets.cc:667 msgid "Popup Image" msgstr "Pop-up Bild" #: f.widgets.cc:668 msgid "Popup Image (add)" msgstr "Pup-up Bild (zusätzlich)" #: f.widgets.cc:672 fotoxx.h:1158 msgid "Rename" msgstr "Umbenennen" #: f.widgets.cc:673 msgid "Copy to Location" msgstr "Zum Verzeichnis kopieren" #: f.widgets.cc:674 msgid "Move to Location" msgstr "Zum Verzeichnis versetzen" #: f.widgets.cc:675 msgid "Copy to Clipboard" msgstr "Auf Zwischenablage kopieren" #: f.widgets.cc:676 msgid "Remove from Album" msgstr "Von Album entfernen" #: f.widgets.cc:677 msgid "Cut to Image Cache" msgstr "zu Bildercache ausschneiden" #: f.widgets.cc:678 msgid "Copy to Image Cache" msgstr "zu Bildercache kopieren" #: f.widgets.cc:679 msgid "Paste Image Cache Here (clear)" msgstr "Bildercache hier einfügen (löschen)" #: f.widgets.cc:680 msgid "Paste Image Cache Here (keep)" msgstr "Bildercache hier einfügen (behalten)" #: f.widgets.cc:681 msgid "Paste Current Image File Here" msgstr "Aktuelle Bilddatei hier einfügen" #: f.widgets.cc:685 msgid "Voodoo Enhance" msgstr "Voodoo Verbesserung" #: f.widgets.cc:690 msgid "Select Area" msgstr "Ausschnitt auswählen" #: f.widgets.cc:693 fotoxx.h:1178 msgid "Trash" msgstr "Abfall" #: f.widgets.cc:819 msgid "Image Locations" msgstr "Bild Orte" #: fotoxx-15.11.cc:373 msgid "Please install missing programs:" msgstr "Bitte fehlende Programme installieren:" #: fotoxx-15.11.cc:662 msgid "Kill active dialog?" msgstr "Aktiv Dialog abbrechen?" #: fotoxx-15.11.cc:733 msgid "(reduced)" msgstr "(verkleinert) " #: fotoxx-15.11.cc:734 msgid "area active" msgstr "Ausschnitt aktiv" #: fotoxx-15.11.cc:735 msgid "dialog open" msgstr "Dialog aktiv" #: fotoxx-15.11.cc:736 msgid "blocked" msgstr "Gesperrt" #: fotoxx-15.11.cc:786 msgid "edits" msgstr "edits" #: fotoxx-15.11.cc:2793 msgid "Exceed 50 anchor points" msgstr "50 Ankerpunkte überschritten" #: fotoxx-15.11.cc:3013 msgid "load curve from a file" msgstr "Kurven-Datei öffnen" #: fotoxx-15.11.cc:3079 msgid "curve file is invalid" msgstr "Kurvendatei ist ungültig" #: fotoxx-15.11.cc:3093 msgid "save curve to a file" msgstr "Kurven-Datei speichern" #: fotoxx-15.11.cc:3165 msgid "Too many edits, please save image" msgstr "Zuviele Änderungen, bitte Bild speichern" #: fotoxx-15.11.cc:3170 msgid "this function cannot be scripted" msgstr "diese Funktion darf nicht in einer Skriptdatei" #: fotoxx-15.11.cc:3187 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Ausschnit nicht aktiviert.\n" "Fortfahren?" #: fotoxx-15.11.cc:3499 msgid "file data does not fit dialog" msgstr "Datei stimmt nicht mit dem Dialog überein" #: fotoxx-15.11.cc:3509 msgid "Save settings to a file" msgstr "Einstellungen in Datei speichern" #: fotoxx-15.11.cc:3825 msgid "This action will discard changes to current image" msgstr "Dieses Vorgehen wird Änderungen im aktuellen Bild verwerfen" #: fotoxx-15.11.cc:3826 msgid "prior function still active" msgstr "Vorherige Funktion noch aktiv" #: fotoxx-15.11.cc:3827 fotoxx.h:1128 msgid "Keep" msgstr "Behalten" #: fotoxx-15.11.cc:3828 msgid "Discard" msgstr "Verwerfen" #: fotoxx.h:1080 msgid "Add" msgstr "Hinzufügen" #: fotoxx.h:1081 msgid "Add All" msgstr "Alle einfügen" #: fotoxx.h:1083 msgid "Amount" msgstr "Wert" #: fotoxx.h:1084 msgid "Angle" msgstr "Winkel" #: fotoxx.h:1085 msgid "Apply" msgstr "Anwenden" #: fotoxx.h:1086 msgid "Auto" msgstr "Auto" #: fotoxx.h:1087 msgid "Black" msgstr "Schwarz" #: fotoxx.h:1088 msgid "Blend Width" msgstr "Mischbreite" #: fotoxx.h:1090 zfuncs.cc:10098 msgid "bottom" msgstr "unten" #: fotoxx.h:1092 msgid "Browse" msgstr "Durchsuchen" #: fotoxx.h:1093 msgid "Cancel" msgstr "Abbrechen" #: fotoxx.h:1094 msgid "Center" msgstr "Mittig" #: fotoxx.h:1095 msgid "Choose" msgstr "Wählen" #: fotoxx.h:1096 msgid "Clear" msgstr "Aufräumen" #: fotoxx.h:1097 msgid "Color" msgstr "Farbe" #: fotoxx.h:1098 msgid "continue" msgstr "fortsetzen" #: fotoxx.h:1100 msgid "Copy" msgstr "Kopieren" #: fotoxx.h:1101 msgid "Create" msgstr "Erstellen" #: fotoxx.h:1102 msgid "Curve File:" msgstr "Kurven-Datei" #: fotoxx.h:1103 msgid "Cut" msgstr "Ausschneiden" #: fotoxx.h:1104 msgid "Deband" msgstr "Entstreifen" #: fotoxx.h:1105 msgid "Delete" msgstr "Löschen" #: fotoxx.h:1107 msgid "Done" msgstr "Fertig" #: fotoxx.h:1108 msgid "edge" msgstr "Rand" #: fotoxx.h:1111 msgid "Erase" msgstr "Löschen" #: fotoxx.h:1112 msgid "Fetch" msgstr "Holen" #: fotoxx.h:1113 msgid "output file already exists" msgstr "Ausgabedatei existiert schon" #: fotoxx.h:1114 #, c-format msgid "%d files selected" msgstr "%d Dateien ausgewählt" #: fotoxx.h:1115 msgid "Find" msgstr "Suchen" #: fotoxx.h:1116 msgid "Finish" msgstr "Fertigstellen" #: fotoxx.h:1117 msgid "Flatten" msgstr "Ausgleichen" #: fotoxx.h:1118 msgid "Font" msgstr "Font" #: fotoxx.h:1119 msgid "Geotags" msgstr "Geotags" #: fotoxx.h:1121 msgid "Grid" msgstr "Gitter" #: fotoxx.h:1122 msgid "Height" msgstr "Höhe" #: fotoxx.h:1125 msgid "Images" msgstr "Bilder" #: fotoxx.h:1126 msgid "Insert" msgstr "Einfügen" #: fotoxx.h:1129 zfuncs.cc:10102 msgid "left" msgstr "links" #: fotoxx.h:1130 msgid "limit" msgstr "Limit" #: fotoxx.h:1131 msgid "Load" msgstr "Laden" #: fotoxx.h:1132 msgid "Make" msgstr "Erstellen" #: fotoxx.h:1134 msgid "Map" msgstr "Karte" #: fotoxx.h:1135 msgid "Max" msgstr "Max" #: fotoxx.h:1136 msgid "Negative" msgstr "Negativ" #: fotoxx.h:1137 msgid "New" msgstr "Neu" #: fotoxx.h:1138 msgid "Next" msgstr "Nächst" #: fotoxx.h:1139 zfuncs.cc:9190 msgid "No" msgstr "Nein" #: fotoxx.h:1140 msgid "no images" msgstr "Keine Bilder" #: fotoxx.h:1142 msgid "no selection" msgstr "Keine Auswahl" #: fotoxx.h:1143 msgid "OK" msgstr "OK" #: fotoxx.h:1145 msgid "Paste" msgstr "Einfügen" #: fotoxx.h:1146 msgid "Pause" msgstr "Unterbrechen" #: fotoxx.h:1147 msgid "Percent" msgstr "Prozent" #: fotoxx.h:1149 msgid "Presets" msgstr "Voreinstellungen" #: fotoxx.h:1150 msgid "Prev" msgstr "Vorig" #: fotoxx.h:1151 msgid "Proceed" msgstr "Weiter" #: fotoxx.h:1153 msgid "range" msgstr "Wertbereich" #: fotoxx.h:1156 msgid "Reduce" msgstr "Vermindern" #: fotoxx.h:1157 msgid "Remove" msgstr "Entfernen" #: fotoxx.h:1159 msgid "Replace" msgstr "Ersetzen" #: fotoxx.h:1160 msgid "Reserved" msgstr "Reserviert" #: fotoxx.h:1161 msgid "Reset" msgstr "Zurücksetzen" #: fotoxx.h:1162 zfuncs.cc:10106 msgid "right" msgstr "rechts" #: fotoxx.h:1163 msgid "Rotate" msgstr "Drehen" #: fotoxx.h:1165 msgid "Unknown file type, save as tiff/jpeg/png to edit" msgstr "Dateityp unbekannt, zum Bearbeiten als tiff/jpeg/png speichern" #: fotoxx.h:1166 msgid "Search" msgstr "Durchsuchen" #: fotoxx.h:1167 msgid "Seconds" msgstr "Sekunden" #: fotoxx.h:1171 msgid "Size" msgstr "Große" #: fotoxx.h:1172 msgid "Start" msgstr "Starten" #: fotoxx.h:1173 msgid "Strength" msgstr "Stärke" #: fotoxx.h:1174 msgid "Threshold" msgstr "Schwelle" #: fotoxx.h:1175 #, c-format msgid "exceed %d files" msgstr "%d Dateien überschritten" #: fotoxx.h:1176 zfuncs.cc:10094 msgid "top" msgstr "oben" #: fotoxx.h:1177 msgid "Transparency" msgstr "Transparenz" #: fotoxx.h:1179 msgid "Trim" msgstr "Schneiden" #: fotoxx.h:1180 msgid "Undo All" msgstr "Alles rückgängig" #: fotoxx.h:1181 msgid "Undo Last" msgstr "Letztes rückgängig" #: fotoxx.h:1183 msgid "Unfinish" msgstr "Unfertigstellen" #: fotoxx.h:1185 msgid "View" msgstr "Ansehen" #: fotoxx.h:1186 msgid "Web" msgstr "Web" #: fotoxx.h:1187 msgid "White" msgstr "Weiß" #: fotoxx.h:1188 msgid "Width" msgstr "Breite" #: fotoxx.h:1189 msgid "x-offset" msgstr "x-Verschiebung" #: fotoxx.h:1190 msgid "y-offset" msgstr "y-Verschiebung" #: fotoxx.h:1191 zfuncs.cc:9190 msgid "Yes" msgstr "Ja" #: zfuncs.cc:1601 #, c-format msgid "" "create directory? \n" " %s" msgstr "" "Verzeichnis erstellen? \n" " %s" #: zfuncs.cc:4378 msgid "user guide not found" msgstr "Benutzeranweisung nicht gefunden" #: zfuncs.cc:5196 #, c-format msgid "cannot open file %s" msgstr "Kann Datei nicht öffnen %s" #: zfuncs.cc:5232 msgid "save screen to file" msgstr "Bildschirm in Datei speichern" #: zfuncs.cc:9266 zfuncs.cc:9761 zfuncs.cc:10081 msgid "cancel" msgstr "Abbrechen" #: zfuncs.cc:9722 msgid "choose file" msgstr "Datei wählen" #: zfuncs.cc:9727 msgid "choose files" msgstr "Dateien wählen" #: zfuncs.cc:9732 msgid "save" msgstr "Speichern" #: zfuncs.cc:9738 msgid "choose folder" msgstr "Verzeichnis wählen" #: zfuncs.cc:9743 msgid "choose folders" msgstr "Verzeichnise wählen" #: zfuncs.cc:9748 msgid "create folder" msgstr "Ordner erstellen" #: zfuncs.cc:9755 msgid "hidden" msgstr "Versteckt" #: zfuncs.cc:10081 msgid "done" msgstr "fertig" #: zfuncs.cc:10081 zfuncs.cc:10090 msgid "margins" msgstr "Ränder" #: zfuncs.cc:10111 msgid "image scale" msgstr "Bildgröße" #: zfuncs.cc:10113 msgid "percent" msgstr "Prozent" #: zfuncs.cc:10120 msgid "image" msgstr "Bild" #: zfuncs.cc:10124 msgid "width" msgstr "Breite" #: zfuncs.cc:10128 msgid "height" msgstr "Höhe" #~ msgid "(0 images)" #~ msgstr "(0 Bilder)" #~ msgid "(%d images)" #~ msgstr "(%d Bilder)" #~ msgid "jpeg quality must be 1-100" #~ msgstr "jpeg Qualität muss 1-100 sein" #~ msgid "Albums: Manage Albums, Slide Show, Desktop Background" #~ msgstr "Alben: Verwalten, Dia-Show, Monitor Hintergrundbild" #~ msgid "Set desktop image from current Fotoxx image" #~ msgstr "Hintergrundbild vom aktuellen Fotoxx Bild bestimmen" #~ msgid "Cycle desktop images from a Fotoxx album" #~ msgstr "Hintergrundbilder wechseln von einem Fotoxx Album" #~ msgid "Set Desktop Image" #~ msgstr "Hintergrundbild setzen" #~ msgid "Cycle Desktop Image" #~ msgstr "Hintergrundbild Wechselzyklus" #~ msgid "Adjust Brightness Dist." #~ msgstr "Helligkeitsverteilung ändern" #~ msgid "" #~ "new name/base/adder unreasonable\n" #~ " e.g. newname ### 100 10" #~ msgstr "Neu Name/Basis/Zuwachs nicht sinnvoll e.g. NeuName ### 100 10" #~ msgid "Lock aspect ratio" #~ msgstr "Breite/Höhe-Verhältnis fixieren" fotoxx-15.11.1/locales/translate-fr.po0000664000175000017500000033320312616075370016250 0ustar micomico# translation of fotoxx_merge.po to # mico , 2008. # Stanislas Zeller , 2008, 2009, 2010, 2011. # translation of fotoxx.po to # #-#-#-#-# fotoxx.po (messages) #-#-#-#-# # French translations for home package. # Copyright (C) 2008 THE home'S COPYRIGHT HOLDER # This file is distributed under the same license as the home package. msgid "" msgstr "" "Project-Id-Version: fotoxx_merge\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-10-28 22:08+0100\n" "PO-Revision-Date: 2015-09-28 13:58+0100\n" "Last-Translator: FR_PUPPY_LINUX \n" "Language-Team: FR_PUPPY_LINUX \n" "Language: fr\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.5.4\n" #: f.albums.cc:78 msgid "" "Right-click album thumbnail to cut/copy \n" "to cache, insert from cache, or remove." msgstr "" "Clic droit sur la vignette de l'album pour couper/copier \n" "dans le cache, insérer à partir du cache ou retirer." #: f.albums.cc:80 msgid "Drag album thumbnail to new position." msgstr "Glisser la vignette de l'album à une nouvelle position." #: f.albums.cc:109 f.widgets.cc:548 msgid "Manage Albums" msgstr "Gestion des Albums" #: f.albums.cc:120 f.albums.cc:258 msgid "Create or replace an album" msgstr "Créer ou remplacer un album" #: f.albums.cc:122 msgid "Album to view or edit" msgstr "Album à afficher ou éditer" #: f.albums.cc:124 msgid "Select images, add to cache" msgstr "Sélectionner les images, ajouter au cache" #: f.albums.cc:126 msgid "Clear image cache" msgstr "Effacer le cache de l'image" #: f.albums.cc:128 f.albums.cc:150 msgid "Delete an album" msgstr "Supprimer un album" #: f.albums.cc:149 msgid "Choose Album" msgstr "Choisir un album" #: f.albums.cc:151 #, c-format msgid "Image cache has %d images" msgstr "" #: f.albums.cc:152 msgid "cache added to empty album" msgstr "cache ajouté à l'album vide" #: f.albums.cc:207 #, c-format msgid "delete %s ?" msgstr "supprimer %s ?" #: f.albums.cc:242 #, c-format msgid "fill from image cache (%d images)" msgstr "remplir à partir du cache de l'image (%d images)" #: f.albums.cc:260 f.albums.cc:297 msgid "Album Name" msgstr "Nom de l'Album" #: f.albums.cc:266 msgid "make an initially empty album" msgstr "créer un album initialement vide" #: f.albums.cc:268 msgid "fill from current gallery" msgstr "remplir à partir de la galerie actuelle" #: f.albums.cc:311 msgid "enter an album name" msgstr "Saisir un nom d'album" #: f.albums.cc:338 msgid "gallery is empty" msgstr "Il n'y a rien dans la galerie" #: f.albums.cc:365 msgid "new album created" msgstr "nouvel album créé" #: f.albums.cc:1099 msgid "Press ESC to exit slide show" msgstr "Appuyer sur la touche Échap pour quitter le diaporama" #: f.albums.cc:1101 msgid "instant" msgstr "instantané" #: f.albums.cc:1102 msgid "fade-in" msgstr "fondu" #: f.albums.cc:1103 msgid "roll-right" msgstr "rouleau à droite" #: f.albums.cc:1104 msgid "roll-down" msgstr "rouleau à gauche" #: f.albums.cc:1105 msgid "venetian" msgstr "store vénitien" #: f.albums.cc:1106 msgid "grate" msgstr "grille" #: f.albums.cc:1107 msgid "rectangle" msgstr "rectangle" #: f.albums.cc:1108 msgid "implode" msgstr "imploser" #: f.albums.cc:1109 msgid "explode" msgstr "exploser" #: f.albums.cc:1110 msgid "radar" msgstr "radar" #: f.albums.cc:1111 msgid "Japanese-fan" msgstr "Fan japonais" #: f.albums.cc:1112 msgid "jaws" msgstr "mâchoires" #: f.albums.cc:1113 msgid "ellipse" msgstr "ellipse" #: f.albums.cc:1114 msgid "raindrops" msgstr "gouttes de pluie" #: f.albums.cc:1115 msgid "doubledoor" msgstr "porte à deux battants" #: f.albums.cc:1116 msgid "rotate" msgstr "pivoter" #: f.albums.cc:1117 msgid "fallover" msgstr "tomber" #: f.albums.cc:1118 msgid "sphereoid" msgstr "" #: f.albums.cc:1139 f.widgets.cc:549 msgid "Slide Show" msgstr "Diaporama" #: f.albums.cc:1150 msgid "Clip Limit" msgstr "Limite d'Attache" #: f.albums.cc:1154 msgid "Music File" msgstr "Fichier Audio" #: f.albums.cc:1159 msgid "Full Screen" msgstr "Plein Écran" #: f.albums.cc:1160 msgid "Auto-replay" msgstr "Répétition auto" #: f.albums.cc:1166 msgid "Customize:" msgstr "Personnaliser:" #: f.albums.cc:1167 msgid "transitions" msgstr "transitions" #: f.albums.cc:1168 msgid "image files" msgstr "fichiers image" #: f.albums.cc:1173 f.albums.cc:1289 #, c-format msgid "%d images" msgstr "%d images" #: f.albums.cc:1195 f.albums.cc:1259 f.albums.cc:1349 f.albums.cc:1462 #: f.albums.cc:1655 f.albums.cc:1739 f.albums.cc:1756 f.albums.cc:1977 msgid "invalid album" msgstr "album non valide" #: f.albums.cc:1273 msgid "open album" msgstr "Ouvrir un album" #: f.albums.cc:1305 msgid "Select music file" msgstr "Sélectionner un fichier musique" #: f.albums.cc:1329 msgid "select random (if 5+ enabled)" msgstr "Sélection aléatoire (si +5 disponibles)" #: f.albums.cc:1353 msgid "Transition Preferences" msgstr "Préférences de Transition " #: f.albums.cc:1362 msgid "transition" msgstr "transition" #: f.albums.cc:1363 msgid "enabled" msgstr "activé" #: f.albums.cc:1364 msgid "slowdown" msgstr "ralentissement" #: f.albums.cc:1365 msgid "preference" msgstr "préférence" #: f.albums.cc:1468 msgid "Image Preferences" msgstr "Préférences Image" #: f.albums.cc:1472 f.file.cc:1159 f.file.cc:1400 f.file.cc:1608 f.meta.cc:437 msgid "Image File:" msgstr "Fichier Image:" #: f.albums.cc:1476 msgid "Play tone when image shows" msgstr "Jouer le son lorsque l'image s'affiche" #: f.albums.cc:1479 msgid "Show image caption" msgstr "Afficher la légende de l'image" #: f.albums.cc:1484 msgid "Show image comments" msgstr "Afficher les commentaires de l'image" #: f.albums.cc:1489 msgid "Wait before zoom" msgstr "Attendre avant le zoom" #: f.albums.cc:1494 msgid "Zoom type:" msgstr "Type de zoom" #: f.albums.cc:1495 msgid "none" msgstr "Aucun" #: f.albums.cc:1496 msgid "zoom-in" msgstr "zoom avant" #: f.albums.cc:1497 msgid "zoom-out" msgstr "zoom arrière" #: f.albums.cc:1500 msgid "Zoom size (x)" msgstr "Taille de zoom (x)" #: f.albums.cc:1503 msgid "Steps" msgstr "Étapes" #: f.albums.cc:1507 msgid "Zoom image location:" msgstr "Emplacement de l'image de zoom" #: f.albums.cc:1515 msgid "Wait after zoom" msgstr "Attendre après le zoom" #: f.albums.cc:1520 msgid "Transition to next image" msgstr "Transition vers l'image suivante" #: f.albums.cc:1523 f.albums.cc:1625 msgid "next" msgstr "suiv." #: f.albums.cc:1962 f.albums.cc:2120 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "erreur de format de fichier: \n" " %s" #: f.area.cc:79 msgid "Select Area for Edits" msgstr "Sélectionner la Zone de Travail" #: f.area.cc:80 f.edit.cc:5927 msgid "Press F1 for help" msgstr "Appuyer sur la touche F1 pour obtenir de l'Aide" #: f.area.cc:89 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Sélection de Zone non prise en charge \n" "par cette fonction d'édition" #: f.area.cc:119 msgid "select rectangle" msgstr "rectangle" #: f.area.cc:120 msgid "select ellipse" msgstr "ellipse" #: f.area.cc:123 msgid "freehand draw" msgstr "dessin à main levée" #: f.area.cc:124 msgid "follow edge" msgstr "suivre le bord" #: f.area.cc:125 msgid "draw/replace" msgstr "dessiner/remplacer" #: f.area.cc:128 msgid "select area within mouse" msgstr "sélectionner une zone avec la souris " #: f.area.cc:131 msgid "select one matching color within mouse" msgstr "sélectionner une couleur correspondante avec la souris" #: f.area.cc:135 msgid "select all matching colors within mouse" msgstr "sélectionner toutes les couleurs correspondantes avec la souris" #: f.area.cc:139 f.edit.cc:5972 msgid "mouse radius" msgstr "rayon du pointeur" #: f.area.cc:142 msgid "match level %" msgstr "niveau de correspondance %" # ?? #: f.area.cc:147 msgid "search range" msgstr "fourchette de recherche" #: f.area.cc:149 msgid "firewall" msgstr "pare-feu" #: f.area.cc:152 msgid "Area Edge Blend Width" msgstr "Largeur de Mélange de Bord de Zone" #: f.area.cc:155 msgid "Edge Creep" msgstr "Fluage du Bord" #: f.area.cc:160 msgid "Line Color:" msgstr "Couleur de ligne" #: f.area.cc:180 f.area.cc:181 msgid "area edits fade away within edge distance" msgstr "les retouches de la zone s'estompent à proximité du bord" #: f.area.cc:360 f.area.cc:543 #, c-format msgid "exceed %d edits" msgstr "excède %d opérations" #: f.area.cc:1364 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 circonscrite \n" "(les éventuelles ruptures de contour seront trouvées). \n" "Appuyer sur la touche F1 pour obtenir de l'Aide." #: f.area.cc:1392 msgid "finish area" msgstr "finir la zone" #: f.area.cc:1400 msgid "extend to corner:" msgstr "étendre jusqu'au coin:" #: f.area.cc:1441 msgid "searching" msgstr "recherche en cours" #: f.area.cc:1472 msgid "outline has a gap" msgstr "le contour est discontinu" #: f.area.cc:1476 msgid "success" msgstr "succès" #: f.area.cc:1613 f.area.cc:1640 #, c-format msgid "found %d pixels" msgstr "%d pixels trouvés" #: f.area.cc:1935 f.area.cc:1952 f.area.cc:1972 f.area.cc:2287 f.area.cc:2363 #: f.area.cc:2520 msgid "the area is not finished" msgstr "la zone n'est pas finie" #: f.area.cc:2061 msgid "Edge calculation in progress" msgstr "calcul du bord en cours" #: f.area.cc:2070 msgid "Area Edge Calc" msgstr "Calcul du Bord de la Zone" #: f.area.cc:2471 msgid "load area from a file" msgstr "Charger la zone à partir d'un fichier" #: f.area.cc:2554 msgid "save area as a PNG file" msgstr "Enregistrer la zone comme fichier PNG" #: f.area.cc:2582 msgid "position with mouse click/drag" msgstr "position par cliquer/glisser" #: f.area.cc:2638 msgid "Paste Image" msgstr "Coller l'Image" #: f.area.cc:2643 f.batch.cc:1169 f.batch.cc:1660 msgid "resize" msgstr "redimensionner" #: f.batch.cc:107 f.widgets.cc:531 msgid "Batch Convert" msgstr "Convertir par Lots" #: f.batch.cc:114 f.file.cc:942 msgid "New Name" msgstr "Nouveau Nom" #: f.batch.cc:118 msgid "Sequence Numbers" msgstr "" #: f.batch.cc:120 msgid "base" msgstr "base" #: f.batch.cc:122 msgid "adder" msgstr "ajout" #: f.batch.cc:127 msgid "New Location" msgstr "Nouvel Emplacement" #: f.batch.cc:132 msgid "New File Type" msgstr "Nouveau Type de Fichier" #: f.batch.cc:136 f.batch.cc:144 f.repair.cc:3861 msgid "no change" msgstr "aucune modification" #: f.batch.cc:139 msgid "max. Width" msgstr "largeur max." #: f.batch.cc:147 f.batch.cc:565 msgid "Delete Originals" msgstr "Supprimer les Originaux" #: f.batch.cc:148 f.batch.cc:562 msgid "Copy Metadata" msgstr "Copier les Métadonnées" #: f.batch.cc:149 f.batch.cc:563 f.edit.cc:1413 f.widgets.cc:438 #: f.widgets.cc:684 msgid "Upright" msgstr "En haut et à droite" #: f.batch.cc:152 f.batch.cc:564 f.batch.cc:1178 f.batch.cc:1669 #: f.repair.cc:118 f.widgets.cc:454 msgid "Sharpen" msgstr "Renforcer la netteté" #: f.batch.cc:154 f.batch.cc:1180 f.batch.cc:1671 msgid "amount" msgstr "quantité" #: f.batch.cc:157 f.batch.cc:1183 f.batch.cc:1674 msgid "threshold" msgstr "seuil" #: f.batch.cc:280 msgid "file type not supported" msgstr "type de fichier non pris en charge" #: f.batch.cc:344 msgid "cannot create new file" msgstr "impossible de créer le nouveau fichier" #: f.batch.cc:391 msgid "updating albums ..." msgstr "mise à jour des albums..." #: f.batch.cc:464 f.batch.cc:1456 f.batch.cc:1862 f.file.cc:1208 msgid "Select directory" msgstr "Sélectionner un répertoire" #: f.batch.cc:513 msgid "" "new name/base/adder unreasonable\n" " e.g. newname ### 100 10\n" " or ... [oldname] ..." msgstr "" #: f.batch.cc:537 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "taille max. %d x %d incohérente" #: f.batch.cc:556 #, c-format msgid "Convert %d image files" msgstr "Convertir les fichiers image %d" #: f.batch.cc:557 msgid "Rename to" msgstr "Renommer en" #: f.batch.cc:558 msgid "Convert to" msgstr "Convertir en" #: f.batch.cc:559 msgid "Resize within" msgstr "Redimensionner dans" #: f.batch.cc:560 msgid "Output to" msgstr "Sortie vers" #: f.batch.cc:566 msgid "PROCEED?" msgstr "POURSUIVRE?" #: f.batch.cc:720 f.widgets.cc:532 msgid "Batch Upright" msgstr "Traitement par Lots Haut-Droit" #: f.batch.cc:726 msgid "Survey all files" msgstr "Surveiller tous les fichiers" #: f.batch.cc:780 msgid "file cannot be read" msgstr "Lecture du fichier impossible" #: f.batch.cc:898 msgid "cannot select both options" msgstr "Impossible de sélectionner les deux options" #: f.batch.cc:942 f.widgets.cc:533 msgid "Batch Delete/Trash" msgstr "Suppression/Mise à la Corbeille par Lots" #: f.batch.cc:947 msgid "delete" msgstr "supprimer" #: f.batch.cc:950 msgid "trash" msgstr "mettre à la corbeille" #: f.batch.cc:1112 msgid "Batch Convert RAW Files (DCraw)" msgstr "Conversion par lots de fichiers RAW (DCraw)" #: f.batch.cc:1121 msgid "DCraw not installed" msgstr "DCraw non installé" #: f.batch.cc:1156 f.batch.cc:1647 msgid "output location" msgstr "emplacement de sortie" #: f.batch.cc:1161 f.batch.cc:1652 msgid "output file type" msgstr "type de fichier de sortie" #: f.batch.cc:1192 msgid "white balance" msgstr "balance des blancs" #: f.batch.cc:1193 msgid "interpolation" msgstr "interpolation" #: f.batch.cc:1194 msgid "color space" msgstr "espace de couleur" #: f.batch.cc:1195 msgid "gamma curve" msgstr "courbe gamma" #: f.batch.cc:1198 msgid "camera" msgstr "Appareil photo" #: f.batch.cc:1199 msgid "fixed" msgstr "défini" #: f.batch.cc:1200 msgid "calculated" msgstr "calculé" #: f.batch.cc:1217 msgid "default" msgstr "Défaut" #: f.batch.cc:1223 msgid "defaults" msgstr "Défauts" #: f.batch.cc:1610 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Conversion par lots de fichiers RAW (Raw Therapee)" #: f.batch.cc:1619 f.file.cc:428 msgid "Raw Therapee not installed" msgstr "Raw Therapee non installé" #: f.batch.cc:1959 msgid "start" msgstr "" #: f.batch.cc:1960 msgid "begin making a script file" msgstr "" #: f.batch.cc:1962 msgid "close" msgstr "" #: f.batch.cc:1963 msgid "finish making a script file" msgstr "" #: f.batch.cc:1965 msgid "run" msgstr "" #: f.batch.cc:1966 msgid "execute a script file" msgstr "" #: f.batch.cc:2020 msgid "script already started" msgstr "" #: f.batch.cc:2024 msgid "open new script file" msgstr "" #: f.batch.cc:2056 msgid "script file error" msgstr "" #: f.batch.cc:2061 #, c-format msgid "%s added to script" msgstr "" #: f.batch.cc:2074 msgid "no script file was started" msgstr "" #: f.batch.cc:2082 msgid "script file closed" msgstr "" #: f.batch.cc:2101 msgid "script file is not closed" msgstr "" #: f.batch.cc:2105 msgid "select script file to run" msgstr "" #: f.batch.cc:2107 msgid "open script file" msgstr "" #: f.batch.cc:2114 msgid "unknown script file" msgstr "" #: f.batch.cc:2121 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" #: f.batch.cc:2132 msgid "select image files to be processed" msgstr "" #: f.batch.cc:2144 f.batch.cc:2193 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" #: f.batch.cc:2165 #, c-format msgid "unknown edit function: %s" msgstr "" #: f.batch.cc:2174 #, c-format msgid "load widgets failed: %s" msgstr "" #: f.batch.cc:2203 #, c-format msgid "script file format error: %s" msgstr "" #: f.batch.cc:2229 msgid "Brasero not installed" msgstr "Brasero non installé" #: f.batch.cc:2318 f.widgets.cc:538 msgid "Find Duplicate Images" msgstr "Chercher les Doublons" #: f.batch.cc:2320 f.tools.cc:929 msgid "Thumbnail size" msgstr "Taille de vignette" #: f.batch.cc:2323 msgid "pixel difference" msgstr "différence de pixel" #: f.batch.cc:2326 msgid "pixel count" msgstr "nombre de pixels" #: f.batch.cc:2329 msgid "Images:" msgstr "Images:" #: f.batch.cc:2330 msgid "searching ..." msgstr "Recherche en cours..." #: f.batch.cc:2332 msgid "Duplicates:" msgstr "Doublons:" #: f.batch.cc:2350 #, c-format msgid "only %d image thumbnails found" msgstr "seulement %d vignettes trouvées" #: f.bend.cc:86 f.widgets.cc:475 msgid "Unbend" msgstr "Décintrer" #: f.bend.cc:94 f.edit.cc:4267 f.effects.cc:4725 msgid "vertical" msgstr "verticale" #: f.bend.cc:95 f.edit.cc:4266 f.effects.cc:4721 msgid "horizontal" msgstr "horizontale" #: f.bend.cc:96 msgid "linear" msgstr "linéaire" #: f.bend.cc:99 msgid "curved" msgstr "cintré" #: f.bend.cc:367 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 sur les quatre coins d'un quadrilatère. Cliquer [Appliquer]. \n" " L'image est déformée pour faire du quadrilatère un rectangle." #: f.bend.cc:384 msgid "Perspective Correction" msgstr "Correction de Perspective" #: f.bend.cc:590 msgid "must have 4 corners" msgstr "doit comporter 4 coins" #: f.bend.cc:712 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 au moyen de la fonction \n" "de sélection de zone. \n" " Cliquer sur [Commencer la déformation] et tirer la zone avec la souris. \n" " Répéter plusieurs fois l'opération jusqu'à satisfaction. \n" " Une fois terminé, sélectionner une autre zone ou cliquer sur [Terminé]." #: f.bend.cc:725 f.widgets.cc:477 msgid "Warp area" msgstr "Déformer la zone" #: f.bend.cc:730 msgid "start warp" msgstr "démarrer la distorsion" #: f.bend.cc:788 msgid "no active Select Area" msgstr "pas de sélection de zone active" #: f.bend.cc:1129 f.bend.cc:1443 msgid "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Tirer à un endroit de l'image au moyen de la souris. \n" " Répéter plusieurs fois l'opération jusqu'à satisfaction. \n" " Une fois terminé, cliquer sur [Terminé]." #: f.bend.cc:1147 f.widgets.cc:478 msgid "Warp curved" msgstr "Décintrer" #: f.bend.cc:1156 f.mashup.cc:1836 msgid "warp span" msgstr "amplitude de distorsion" #: f.bend.cc:1461 f.widgets.cc:479 msgid "Warp linear" msgstr "Distorsion linéaire" #: f.bend.cc:1776 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 au moyen de la souris. \n" " Répéter plusieurs fois l'opération jusqu'à satisfaction. \n" " Une fois terminé, cliquer sur [Terminé]." #: f.bend.cc:1792 f.widgets.cc:480 msgid "Warp affine" msgstr "Déformation affinée" #: f.bend.cc:2138 msgid "Flatten Book Page Photo" msgstr "Aplatir une Photo de Page de Livre" #: f.bend.cc:2139 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" "Mapper les bords supérieur et inférieur avec \n" "4+ clics de souris, puis aplatir:" #: f.bend.cc:2142 msgid "Stretch curved-down surfaces:" msgstr "Étirer les surfaces cintrées vers le bas:" #: f.bend.cc:2197 msgid "top:" msgstr "haut:" #: f.bend.cc:2200 msgid "bottom:" msgstr "bas:" #: f.combine.cc:2108 f.combine.cc:2740 f.combine.cc:3396 f.combine.cc:3928 msgid "Select 2 to 9 files" msgstr "Sélectionner 2 à 9 fichiers" #: f.combine.cc:2129 f.combine.cc:2761 f.combine.cc:3417 f.combine.cc:3949 msgid "Images are not all the same size" msgstr "Les images ne sont pas toutes de taille identique" #: f.combine.cc:2473 msgid "Adjust Image Contributions" msgstr "Ajuster les Recouvrements de l'Image" #: f.combine.cc:2477 msgid "dark pixels" msgstr "pixels foncés" #: f.combine.cc:2479 msgid "light pixels" msgstr "pixels clairs" #: f.combine.cc:2481 msgid "file:" msgstr "fichier: " #: f.combine.cc:2997 msgid "Paint and Warp Image" msgstr "Peindre et Déformer l'Image" #: f.combine.cc:3001 f.combine.cc:3646 f.combine.cc:6237 f.effects.cc:3912 #: f.widgets.cc:567 f.widgets.cc:590 f.widgets.cc:612 msgid "Image" msgstr "Image" #: f.combine.cc:3005 msgid "paint" msgstr "peindre" #: f.combine.cc:3006 msgid "warp" msgstr "déformer" #: f.combine.cc:3644 msgid "Select and Paint Image" msgstr "Sélectionner et Peindre l'Image" #: f.combine.cc:4120 msgid "Adjust Pixel Composition" msgstr "Ajuster la Composition des Pixels" #: f.combine.cc:4122 msgid "use average" msgstr "utiliser moyenne" #: f.combine.cc:4123 msgid "use median" msgstr "utiliser médiane" #: f.combine.cc:4125 msgid "omit low pixel" msgstr "exclure les pixels bas" #: f.combine.cc:4126 msgid "omit high pixel" msgstr "exclure les pixels hauts" #: f.combine.cc:4388 f.combine.cc:5494 msgid "Select 2 to 4 files" msgstr "Sélectionner 2 à 4 fichiers" #: f.combine.cc:4463 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from lower edge." msgstr "" "Glisser les images dans un alignement approximatif.\n" "Pour pivoter, faire glisser depuis le bord inférieur." #: f.combine.cc:4465 f.combine.cc:5571 msgid "no curve (scanned image)" msgstr "aucune courbe (image scannée)" #: f.combine.cc:4466 f.combine.cc:5572 msgid "Search for lens mm" msgstr "Rechercher focale" #: f.combine.cc:4467 f.combine.cc:5573 msgid "Save lens mm → image EXIF" msgstr "Enregistrer la focale mm → image EXIF" #: f.combine.cc:4528 f.combine.cc:5634 msgid "Pre-align Images" msgstr "Pré-aligner les Images" #: f.combine.cc:4532 f.combine.cc:5638 msgid "lens mm" msgstr "longueur focale (lens mm)" #: f.combine.cc:4537 f.combine.cc:5643 msgid "no auto warp" msgstr "pas d'auto-déformation" #: f.combine.cc:4539 f.combine.cc:5645 f.widgets.cc:445 f.widgets.cc:683 msgid "Resize" msgstr "Redimensionner" #: f.combine.cc:4540 f.combine.cc:5646 msgid "resize window" msgstr "redimensionner la fenêtre" #: f.combine.cc:4548 f.combine.cc:5654 msgid "do not warp images during auto-alignment" msgstr "ne pas déformer les images pendant l'auto-alignement" #: f.combine.cc:4593 f.combine.cc:5699 msgid "use two images only" msgstr "n'utiliser que deux images" #: f.combine.cc:4621 f.combine.cc:4825 f.combine.cc:5011 f.combine.cc:5725 #: f.combine.cc:5929 f.combine.cc:6115 msgid "Too little overlap, cannot align" msgstr "Chevauchement insuffisant, alignement impossible." #: f.combine.cc:5089 f.combine.cc:6193 msgid "Match Brightness and Color" msgstr "Faire correspondre Luminosité et Couleur" #: f.combine.cc:5134 msgid "Select image" msgstr "Sélectionner l'image" #: f.combine.cc:5149 f.combine.cc:6252 msgid "auto color" msgstr "couleur automatique" #: f.combine.cc:5150 f.combine.cc:6253 msgid "file color" msgstr "couleur du fichier" #: f.combine.cc:5156 f.combine.cc:6259 msgid "mouse warp" msgstr "déformation à la souris" #: f.combine.cc:5159 msgid "curved image" msgstr "image courbée" #: f.combine.cc:5569 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from right edge." msgstr "" "Glisser les images dans un alignement approximatif. \n" "Pour pivoter, faire glisser depuis le bord droit." #: f.combine.cc:6469 msgid "pano tools (hugin) not installed" msgstr "Outils pano (hugin) non installés" #: f.combine.cc:6478 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: Glisser au centre pour déplacer, tirer les coins pour redimensionner" #: f.edit.cc:99 msgid "Minor rotate: drag right edge with mouse" msgstr "Légère rotation: Tirer le coin droit avec la souris" #: f.edit.cc:173 f.widgets.cc:437 f.widgets.cc:682 msgid "Trim/Rotate" msgstr "Rogner/Pivoter" #: f.edit.cc:185 f.edit.cc:586 msgid "ratio" msgstr "proportions" #: f.edit.cc:189 msgid "trim size:" msgstr "taille de rognage:" #: f.edit.cc:194 msgid "Lock Ratio" msgstr "Verrouiller les Proportions" #: f.edit.cc:199 msgid "Customize" msgstr "Personnaliser" #: f.edit.cc:207 msgid "Rotate: degrees" msgstr "Pivoter: degrés" #: f.edit.cc:209 msgid "auto-trim" msgstr "rognage automatique" #: f.edit.cc:582 msgid "Trim Buttons" msgstr "Boutons de Rognage" #: f.edit.cc:585 msgid "label" msgstr "étiquette" #: f.edit.cc:1411 msgid "Upright Image" msgstr "Redresser l'Image" #: f.edit.cc:1477 msgid "rotation unknown" msgstr "rotation inconnue" #: f.edit.cc:2029 f.widgets.cc:441 f.widgets.cc:686 msgid "Retouch Combo" msgstr "Combo de Retouche" #: f.edit.cc:2050 msgid "Amplifier" msgstr "Amplificateur" #: f.edit.cc:2051 fotoxx.h:1091 msgid "Brightness" msgstr "Luminosité" #: f.edit.cc:2052 f.effects.cc:3364 fotoxx.h:1099 msgid "Contrast" msgstr "Contraste" #: f.edit.cc:2053 msgid "Low Color" msgstr "Couleur Basse" #: f.edit.cc:2054 msgid "Warmer" msgstr "Plus chaud" #: f.edit.cc:2055 f.effects.cc:1011 msgid "Dark Areas" msgstr "Zones Foncées" #: f.edit.cc:2064 msgid "Max." msgstr "Max." #: f.edit.cc:2065 f.edit.cc:2066 f.edit.cc:2067 msgid "High" msgstr "Haute" #: f.edit.cc:2068 msgid "Cooler" msgstr "Plus froid" #: f.edit.cc:2069 msgid "Bright" msgstr "Clair" #: f.edit.cc:2072 f.tools.cc:1631 msgid "Brightness Distribution" msgstr "Répartition de la luminosité" #: f.edit.cc:2075 msgid "Click for white balance or black level" msgstr "Cliquer pour la balance des niveaux de blanc ou de noir" #: f.edit.cc:2078 msgid "Settings File" msgstr "Fichier de Paramétrage" #: f.edit.cc:2082 msgid "recall previous settings used" msgstr "rappeler les paramètres utilisés précédemment" #: f.edit.cc:2726 msgid "Adjust Brightness Distribution" msgstr "Ajuster la Répartition de la Luminosité" #: f.edit.cc:2765 msgid "Low Cutoff" msgstr "Coupure Légère" #: f.edit.cc:2766 msgid "High Cutoff" msgstr "Coupure Élevée" #: f.edit.cc:2767 msgid "Low Flatten" msgstr "Aplatissement Léger" #: f.edit.cc:2768 msgid "Mid Flatten" msgstr "Aplatissement Moyen" #: f.edit.cc:2769 msgid "High Flatten" msgstr "Aplatissement Élevé" #: f.edit.cc:2770 msgid "Low Stretch" msgstr "Étirement Léger" #: f.edit.cc:2771 msgid "Mid Stretch" msgstr "Étirement Moyen" #: f.edit.cc:2772 msgid "High Stretch" msgstr "Étirement Élevé" #: f.edit.cc:3102 msgid "Zonal Flatten Brightness" msgstr "Aplatissement de Luminosité Zonal" #: f.edit.cc:3135 msgid "Zones" msgstr "" #: f.edit.cc:3142 msgid "Deband Dark" msgstr "Estomper Sombre" #: f.edit.cc:3145 msgid "Deband Bright" msgstr "Estomper Clair" #: f.edit.cc:3594 f.widgets.cc:444 f.widgets.cc:689 msgid "Tone Mapping" msgstr "Mappage de Tons" #: f.edit.cc:3625 msgid "low" msgstr "faible" #: f.edit.cc:3627 msgid "high" msgstr "haute" #: f.edit.cc:3630 msgid "Amplify" msgstr "Intensifier" #: f.edit.cc:4034 msgid "Resize Image" msgstr "Redimensionner l'Image" #: f.edit.cc:4059 msgid "W/H Ratio:" msgstr "" #: f.edit.cc:4061 msgid "Lock" msgstr "" #: f.edit.cc:4063 msgid "use previous settings" msgstr "" #: f.edit.cc:4262 f.widgets.cc:446 msgid "Flip" msgstr "Miroir" #: f.edit.cc:4368 msgid "+Version" msgstr "+Version" #: f.edit.cc:4391 msgid "Write Text on Image" msgstr "Écrire du Texte sur l'Image" #: f.edit.cc:4392 msgid "Enter text, click/drag on image, right click to remove" msgstr "" "Saisir du texte, cliquer/glisser sur l'image, clic-droit pour retirer le " "texte." #: f.edit.cc:4441 f.mashup.cc:2437 msgid "Text" msgstr "Texte" #: f.edit.cc:4446 msgid "Use metadata key" msgstr "Utiliser la clé des métadonnées" #: f.edit.cc:4451 msgid "Use text file" msgstr "Utiliser le fichier texte" #: f.edit.cc:4470 f.mashup.cc:2455 msgid "text" msgstr "texte" #: f.edit.cc:4471 f.edit.cc:5309 f.mashup.cc:2456 f.mashup.cc:2798 msgid "backing" msgstr "appui" #: f.edit.cc:4472 f.edit.cc:5310 f.mashup.cc:2457 f.mashup.cc:2799 msgid "outline" msgstr "contour" #: f.edit.cc:4473 f.edit.cc:5311 f.mashup.cc:2458 f.mashup.cc:2800 msgid "shadow" msgstr "ombre" #: f.edit.cc:4499 msgid "save to current file" msgstr "enregistrer dans le fichier actuel" #: f.edit.cc:4500 f.file.cc:1909 msgid "save as new file version" msgstr "enregistrer comme nouvelle version du fichier" #: f.edit.cc:4501 msgid "" "save to current file \n" "open next file with same text" msgstr "" "enregistrer dans le fichier actuel \n" "ouvrir le fichier suivant avec le le même texte" #: f.edit.cc:4662 f.mashup.cc:2557 f.tools.cc:1068 msgid "select font" msgstr "sélectionner la police" #: f.edit.cc:4926 msgid "text file is defective" msgstr "fichier texte défectueux" #: f.edit.cc:5249 msgid "" "Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove" msgstr "" "Saisir les propriétés de ligne ou de flèche dans la boite de dialogue, \n" "cliquer/glisser sur l'image, clic droit pour effacer" #: f.edit.cc:5280 msgid "Write Line or Arrow on Image" msgstr "Dessiner une Ligne ou une Flèche sur l'Image" #: f.edit.cc:5288 f.mashup.cc:2777 msgid "Line length" msgstr "Longueur de la ligne" #: f.edit.cc:5295 f.mashup.cc:2784 msgid "Arrow head" msgstr "Pointe de la flèche" #: f.edit.cc:5308 f.mashup.cc:2797 msgid "line" msgstr "ligne" #: f.edit.cc:5337 msgid "" "fix line/arrow in layout \n" " start new line/arrow" msgstr "" "fixer la ligne/flèche dans le montage \n" " commencer une nouvelle ligne/flèche" #: f.edit.cc:5926 f.widgets.cc:449 msgid "Paint Edits" msgstr "Opérations de Peinture" #: f.edit.cc:5933 f.edit.cc:6162 fotoxx-15.11.cc:3179 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "Impossible de conserver la sélection de zone.\n" "Voulez-vous continuer?" #: f.edit.cc:5941 f.edit.cc:6170 f.tools.cc:2157 msgid "Edit function must be active" msgstr "La fonction d'édition doit être active" #: f.edit.cc:5946 msgid "Cannot use Paint Edits" msgstr "Utilisation des Éditions de Peinture Impossible" #: f.edit.cc:5975 msgid "power: center" msgstr "puissance: centre" #: f.edit.cc:5980 msgid "reset area" msgstr "réinitialiser la zone" #: f.edit.cc:6155 f.widgets.cc:450 msgid "Leverage Edits" msgstr "Retouches à Effets zonevier" #: f.edit.cc:6156 msgid "Edit Function Amplifier" msgstr "Éditer l'Amplificateur de Fonction" #: f.edit.cc:6175 msgid "Cannot use Leverage Edits" msgstr "Impossible d'utiliser les ÉdItions de Force zonevier" #: f.edit.cc:6208 msgid "minimum" msgstr "minimum" #: f.edit.cc:6210 msgid "maximum" msgstr "maximum" #: f.edit.cc:6510 msgid "Edit Plugins" msgstr "Éditer les Greffons" #: f.edit.cc:6511 msgid "Edit plugins menu" msgstr "Éditer le menu des greffons" #: f.edit.cc:6519 msgid "Run as Fotoxx edit function" msgstr "Exécuter comme fonction d'édition de Fotoxx" #: f.edit.cc:6757 msgid "Plugin working ..." msgstr "Greffon en cours d'exécution..." #: f.edit.cc:6766 msgid "plugin failed" msgstr "Échec de l’exécution du greffon" #: f.effects.cc:64 msgid "Set color depth to 1-16 bits" msgstr "" "Définir la profondeur de \n" "couleur de 1 à 16 bit(s)." #: f.effects.cc:76 msgid "Set Color Depth" msgstr "Définir la profondeur de couleurs" #: f.effects.cc:220 msgid "Convert to Sketch" msgstr "Transformer en Esquisse" #: f.effects.cc:296 msgid "Clip Level" msgstr "Niveau d'Attache" #: f.effects.cc:300 msgid "Algorithm" msgstr "Algorithme" #: f.effects.cc:305 msgid "Foreground" msgstr "Premier-Plan" #: f.effects.cc:308 msgid "Background" msgstr "Arrière-Plan" #: f.effects.cc:714 f.widgets.cc:486 msgid "Line Drawing" msgstr "Tracer des Lignes" #: f.effects.cc:727 msgid "black/white" msgstr "noir/blanc" #: f.effects.cc:1005 f.widgets.cc:487 msgid "Color Drawing" msgstr "Dessiner une Couleur" #: f.effects.cc:1012 msgid "Bright Areas" msgstr "Zones claires" #: f.effects.cc:1266 f.widgets.cc:488 msgid "Graduated Blur" msgstr "Flou Progressif" #: f.effects.cc:1270 msgid "Contrast Limit" msgstr "Limite de Contraste" #: f.effects.cc:1273 f.repair.cc:775 msgid "Blur Radius" msgstr "Rayon du Flou" #: f.effects.cc:1487 msgid "Simulate Embossing" msgstr "Estampage" #: f.effects.cc:1504 msgid "depth" msgstr "profondeur" #: f.effects.cc:1706 msgid "Simulate Tiles" msgstr "Mosaïque" #: f.effects.cc:1710 msgid "tile size" msgstr "taille du carreau" #: f.effects.cc:1713 msgid "tile gap" msgstr "espace entre les carreaux" #: f.effects.cc:1716 msgid "3D depth" msgstr "profondeur 3D " #: f.effects.cc:1940 msgid "Convert Image to Dots" msgstr "Tramer l'image en Points" #: f.effects.cc:1944 msgid "dot size" msgstr "taille du point" #: f.effects.cc:2169 msgid "Simulate Painting" msgstr "Simuler une Peinture" #: f.effects.cc:2173 msgid "color depth" msgstr "profondeur de couleurs" #: f.effects.cc:2177 msgid "patch area goal" msgstr "corriger la zone cible" #: f.effects.cc:2181 msgid "req. color match" msgstr "couleur correspondante requise" #: f.effects.cc:2185 msgid "borders" msgstr "bordures" #: f.effects.cc:2758 f.widgets.cc:493 msgid "Vignette" msgstr "Instantané" #: f.effects.cc:3107 msgid "Add Texture" msgstr "Ajouter Texture" #: f.effects.cc:3324 msgid "Background Pattern" msgstr "Motif de fond" #: f.effects.cc:3328 msgid "Pattern File:" msgstr "Fichier de Motif:" #: f.effects.cc:3333 msgid "Geometry" msgstr "Géométrie" #: f.effects.cc:3334 msgid "Calculate" msgstr "Calculer" #: f.effects.cc:3336 msgid "Zoom" msgstr "Zoom" #: f.effects.cc:3347 f.widgets.cc:495 msgid "Pattern" msgstr "Motif" #: f.effects.cc:3354 msgid "Overlap" msgstr "Chevauchement" #: f.effects.cc:3361 msgid "Opacity" msgstr "Opacité" #: f.effects.cc:3429 msgid "choose pattern tile" msgstr "choisir le fichier de motif" #: f.effects.cc:3816 msgid "Create Mosaic" msgstr "Créer une Mosaïque" #: f.effects.cc:3902 msgid "Tile" msgstr "Motif" #: f.effects.cc:3910 f.widgets.cc:490 msgid "Tiles" msgstr "Mosaïque" #: f.effects.cc:3916 msgid "Tile blending" msgstr "Mélange de motifs" #: f.effects.cc:3996 #, c-format msgid "exceeded max. tiles: %d" msgstr "excèzone maximum de motifs: %d" #: f.effects.cc:4011 #, c-format msgid "only %d tile images found" msgstr "seulement %d images de motifs trouvés" #: f.effects.cc:4404 f.widgets.cc:497 msgid "Custom Kernel" msgstr "Noyau personnalisé" #: f.effects.cc:4408 msgid "Kernel size" msgstr "Taille du noyau" #: f.effects.cc:4411 msgid "Divisor" msgstr "Diviseur" #: f.effects.cc:4414 msgid "Data file" msgstr "Fichier de données" #: f.effects.cc:4450 fotoxx-15.11.cc:3440 msgid "Load settings from file" msgstr "Charger les paramètres à partir d'un fichier" #: f.effects.cc:4711 f.widgets.cc:498 msgid "Make Waves" msgstr "Onduler" #: f.effects.cc:4718 msgid "wavelength" msgstr "longueur de vague" #: f.effects.cc:4719 msgid "amplitude" msgstr "amplitude" #: f.effects.cc:4720 msgid "variance" msgstr "écart" #: f.effects.cc:4731 msgid "perspective" msgstr "perspective" #: f.effects.cc:4905 msgid "Pull image using the mouse." msgstr "Tirer l'image avec la souris" #: f.effects.cc:4926 f.widgets.cc:499 msgid "Directed Blur" msgstr "Flou Directionnel" #: f.effects.cc:4930 msgid "blur span" msgstr "période de flou" #: f.effects.cc:4933 msgid "intensity" msgstr "intensité" #: f.effects.cc:5099 f.widgets.cc:500 msgid "Spherical Projection" msgstr "" #: f.effects.cc:5143 msgid "Magnify" msgstr "" #: f.file.cc:186 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:203 f.widgets.cc:572 msgid "File" msgstr "Fichier" #: f.file.cc:357 msgid "UFraw not installed" msgstr "UFraw non installé" #: f.file.cc:373 f.widgets.cc:691 msgid "Open RAW file (ufraw)" msgstr "Ouvrir un fichier RAW (ufraw)" #: f.file.cc:378 f.file.cc:449 msgid "RAW type not registered in User Settings" msgstr "Type RAW non répertorié dans les Paramètres Utilisateur" #: f.file.cc:444 f.widgets.cc:692 msgid "Open RAW file (Raw Therapee)" msgstr "Ouvrir un fichier RAW (Raw Therapee)" #: f.file.cc:541 f.widgets.cc:395 msgid "Open Image File" msgstr "Ouvrir un Fichier Image" #: f.file.cc:560 msgid "unknown file type" msgstr "type de fichier inconnu" #: f.file.cc:753 msgid "Create Blank Image" msgstr "Créer une Image Vierge" #: f.file.cc:755 msgid "file name" msgstr "nom du fichier" #: f.file.cc:788 msgid "supply a file name" msgstr "Fournir un nom de fichier" #: f.file.cc:934 f.widgets.cc:400 msgid "Rename Image File" msgstr "Renommer le Fichier Image" #: f.file.cc:941 msgid "Old Name" msgstr "Ancien Nom" #: f.file.cc:951 msgid "previous name" msgstr "nom précédent" #: f.file.cc:952 msgid "add 1" msgstr "Plus 1" #: f.file.cc:1120 msgid "Copy Image File" msgstr "Copier un Fichier Image" #: f.file.cc:1121 msgid "Move Image File" msgstr "Déplacer un Fichier Image" #: f.file.cc:1164 msgid "new location" msgstr "nouvel emplacement" #: f.file.cc:1236 msgid "new location is not a directory" msgstr "le nouvel emplacement n'est pas un répertoire" #: f.file.cc:1276 f.file.cc:1518 f.file.cc:1656 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "échec de la suppression: \n" " %s" #: f.file.cc:1363 f.widgets.cc:401 msgid "Trash Image File" msgstr "Mettre le Fichier Image à la Corbeille" #: f.file.cc:1364 f.file.cc:1571 msgid "(automatic step to next image)" msgstr "(passer automatiquement à l'image suivante)" #: f.file.cc:1432 msgid "" "Linux standard trash does not work. \n" "Desktop trash folder will be created." msgstr "" "La corbeille standard Linux n'est pas prise en charge. \n" "Un dossier Corbeille sera créé." #: f.file.cc:1435 msgid "" "Linux and Desktop trash do not work. \n" "Permanently delete the image file?" msgstr "" "Linux et la Corbeille du bureau ne fonctionnent pas. \n" "Supprimer définitivement le fichier image?" #: f.file.cc:1438 #, c-format msgid "Cannot create trash folder: %s" msgstr "Impossible de créer le dossier Corbeille: %s" #: f.file.cc:1450 msgid "Move read-only file to trash?" msgstr "Déplacer le fichier en lecture-seule vers la Corbeille?" #: f.file.cc:1505 #, c-format msgid "error: %s" msgstr "erreur: %s" #: f.file.cc:1534 f.file.cc:1673 f.file.cc:2508 msgid "no more images" msgstr "plus d'images" #: f.file.cc:1570 msgid "Delete Image File - CANNOT BE REVERSED" msgstr "Supprimer le Fichier Image - ACTION IRRÉVERSIBLE" #: f.file.cc:1646 msgid "Delete read-only file?" msgstr "Supprimer le fichier en lecture seule?" #: f.file.cc:1891 msgid "Save Image File" msgstr "Enregistrer le Fichier Image" #: f.file.cc:1901 msgid "new version" msgstr "nouvelle version" #: f.file.cc:1902 msgid "new file" msgstr "nouveau fichier" #: f.file.cc:1903 msgid "replace file" msgstr "remplacer le fichier" #: f.file.cc:1910 f.file.cc:2281 msgid "save as new file name or type" msgstr "enregistrer sous un nouveau nom ou type de fichier" #: f.file.cc:1912 msgid "replace old file (OVERWRITE)" msgstr "remplacer l'ancien fichier (ÉCRASER)" #: f.file.cc:1950 f.file.cc:2057 msgid "cannot save as RAW type" msgstr "enregistrement en type RAW impossible" #: f.file.cc:2051 msgid "" "Transparency map will be lost.\n" "save to PNG file to retain." msgstr "" "La carte de transparence sera perdue.\n" "enregisrer dans un fichier PNG pour la conserver." #: f.file.cc:2191 msgid "Unable to copy EXIF/IPTC data" msgstr "Copie des données EXIF/IPTC impossible" #: f.file.cc:2299 f.file.cc:2302 msgid "make current" msgstr "faire l’actuelle" #: f.file.cc:2300 msgid "(new file becomes current file)" msgstr "" #: f.file.cc:2409 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Écraser le fichier ? \n" " %s" #: f.file.cc:2555 f.widgets.cc:554 msgid "Quick Start" msgstr "Démarrage Rapide" #: f.file.cc:2558 f.widgets.cc:555 msgid "User Guide" msgstr "Manuel Utilisateur" #: f.file.cc:2561 f.widgets.cc:556 msgid "User Guide Changes" msgstr "Changements du Manuel Utilisateur" #: f.file.cc:2564 f.widgets.cc:558 msgid "README" msgstr "LISEZ-MOI" #: f.file.cc:2567 f.widgets.cc:557 msgid "Edit Functions Summary" msgstr "Éditer le Sommaire des Fonctions" #: f.file.cc:2570 f.widgets.cc:559 msgid "Change Log" msgstr "Journal des Changements" #: f.file.cc:2573 f.widgets.cc:560 msgid "Log File" msgstr "Fichier-Journal" #: f.file.cc:2576 f.widgets.cc:561 msgid "Translations" msgstr "Traductions" #: f.file.cc:2579 f.widgets.cc:562 msgid "Home Page" msgstr "Site web" #: f.file.cc:2582 f.widgets.cc:563 msgid "About" msgstr "À propos" #: f.file.cc:2586 f.widgets.cc:584 f.widgets.cc:606 fotoxx.h:1123 msgid "Help" msgstr "Aide" #: f.file.cc:2739 f.file.cc:2808 #, c-format msgid "file not found: %s" msgstr "fichier introuvable: %s" #: f.file.cc:2745 f.file.cc:2814 #, c-format msgid "file type not supported: %s" msgstr "type de fichier non pris en charge: %s" #: f.gallery.cc:918 f.gallery.cc:1007 f.widgets.cc:604 msgid "Scroll" msgstr "Faire défiler" #: f.gallery.cc:922 msgid "Sync" msgstr "Sync" #: f.gallery.cc:932 f.repair.cc:5845 f.repair.cc:5850 fotoxx.h:1144 msgid "Open" msgstr "Ouvrir" #: f.gallery.cc:933 msgid "change directory" msgstr "changer de répertoire" #: f.gallery.cc:941 msgid "GoTo" msgstr "Aller à" #: f.gallery.cc:947 f.widgets.cc:599 msgid "Sort" msgstr "Trier" #: f.gallery.cc:953 f.gallery.cc:1517 f.gallery.cc:1518 f.gallery.cc:1520 #: f.widgets.cc:597 msgid "Zoom+" msgstr "Zoom+" #: f.gallery.cc:963 f.gallery.cc:1519 f.gallery.cc:1521 f.widgets.cc:598 msgid "Zoom-" msgstr "Zoom-" #: f.gallery.cc:975 f.gallery.cc:1525 msgid "Row↑" msgstr "Rangée↑" #: f.gallery.cc:983 f.gallery.cc:1526 msgid "Row↓" msgstr "Rangée↓" #: f.gallery.cc:991 f.gallery.cc:1523 f.gallery.cc:1530 f.widgets.cc:602 msgid "Page↑" msgstr "Page↑" #: f.gallery.cc:999 f.gallery.cc:1524 f.gallery.cc:1531 f.widgets.cc:603 msgid "Page↓" msgstr "Page↓" #: f.gallery.cc:1013 f.gallery.cc:1528 f.widgets.cc:600 msgid "First" msgstr "Première" #: f.gallery.cc:1014 f.gallery.cc:1529 f.widgets.cc:601 msgid "Last" msgstr "Dernière" #: f.gallery.cc:1127 msgid "Choose image directory" msgstr "Choisir le répertoire image" #: f.gallery.cc:1134 f.gallery.cc:1148 msgid "recent" msgstr "récents" #: f.gallery.cc:1135 f.gallery.cc:1153 msgid "newest" msgstr "derniers" #: f.gallery.cc:1217 msgid "no albums found" msgstr "Aucun album" #: f.gallery.cc:1224 msgid "Choose album" msgstr "Choisir l'album" #: f.gallery.cc:1274 msgid "Gallery Sort" msgstr "Trier la Galerie" #: f.gallery.cc:1278 msgid "File Name" msgstr "Nom du Fichier" #: f.gallery.cc:1279 msgid "File Mod Date/Time" msgstr "Mode Date/Heure du Fichier" #: f.gallery.cc:1280 msgid "Photo Date/Time (EXIF)" msgstr "Date/Heure de la Photo (EXIF)" #: f.gallery.cc:1282 msgid "ascending" msgstr "croissant" #: f.gallery.cc:1283 msgid "descending" msgstr "décroissant" #: f.gallery.cc:2212 fotoxx.h:1169 msgid "Select Files" msgstr "Sélectionner les Fichiers" #: f.gallery.cc:2672 msgid "Click list position. Click thumbnail to add." msgstr "Cliquer sur l'endroit de la liste. Cliquer sur la vignette à ajouter." #: f.gallery.cc:2698 f.gallery.cc:2946 msgid "Edit Bookmarks" msgstr "Éditer les signets" #: f.gallery.cc:2873 msgid "unable to save bookmarks file" msgstr "impossible d'enregistrer le fichier des signets" #: f.gallery.cc:2946 msgid "Go To Bookmark" msgstr "Aller Au Signet" #: f.gallery.cc:3003 f.meta.cc:2844 f.repair.cc:7473 msgid "file not found" msgstr "fichier introuvable" #: f.mashup.cc:170 msgid "Mashup layout and background image" msgstr "Montage Pêle-Mêle 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 actuel" #: f.mashup.cc:215 msgid "specify layout size and color" msgstr "spécifier la taille et la couleur du montage" #: f.mashup.cc:216 msgid "open a Mashup project file" msgstr "ouvrir un fichier de projet de Pêle-Mêle" #: f.mashup.cc:241 msgid "choose layout file" msgstr "choisir le fichier de montage" #: f.mashup.cc:259 msgid "no current file" msgstr "aucun fichier courant" #: f.mashup.cc:291 msgid "Make Layout Image" msgstr "Créer un Montage d'Images" #: 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:417 msgid "Edit Images" msgstr "Éditer les Images" #: f.mashup.cc:418 f.mashup.cc:2432 msgid "Edit Text" msgstr "Éditer le Texte" #: f.mashup.cc:419 msgid "Edit Line" msgstr "Éditer la Ligne" #: f.mashup.cc:420 msgid "Rescale" msgstr "Remettre à l'échelle" #: f.mashup.cc:423 msgid "add or edit images" msgstr "ajouter ou éditer des images" #: f.mashup.cc:424 msgid "add or edit text" msgstr "ajouter ou éditer le texte" #: f.mashup.cc:425 msgid "add or edit lines/arrows" msgstr "ajouter ou modifier les lignes/flèches" #: f.mashup.cc:426 msgid "change project scale" msgstr "Modifier l'échelle du projet" #: f.mashup.cc:427 msgid "project complete" msgstr "projet complet" #: f.mashup.cc:428 msgid "cancel project" msgstr "abandonner le projet" #: f.mashup.cc:446 msgid "rescale project" msgstr "Mettre le projet à l'échelle" #: f.mashup.cc:501 msgid "save Mashup output file" msgstr "enregistrer le fichier de sortie du Pêle-Mêle" #: f.mashup.cc:512 msgid "save Mashup project file?" msgstr "enregistrer le fichier projet de Pête-Mêle?" #: f.mashup.cc:524 msgid "delete Mashup project file?" msgstr "supprimer le fichier projet du Pêle-Mêle?" #: f.mashup.cc:583 msgid "Open Project" msgstr "Ouvrir un Projet" #: f.mashup.cc:612 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "fichier image de montage manquant: \n" "%s" #: f.mashup.cc:669 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "fichier image de superposition manquant: \n" "%s" #: f.mashup.cc:974 msgid "project file is defective" msgstr "fichier projet défectueux" #: f.mashup.cc:1004 msgid "Save Project" msgstr "Enregistrer le Projet" #: f.mashup.cc:1145 msgid "layout exceeds 2 gigabytes" msgstr "le montage dépasse 2 gigaoctets" #: f.mashup.cc:1219 msgid "Click image to select, drag image to move." msgstr "Cliquer l'image pour sélectionner, glisser l'image pour déplacer" #: f.mashup.cc:1220 msgid "Make black margins transparent" msgstr "Rendre les marges noires transparentes" #: f.mashup.cc:1259 msgid "Current image:" msgstr "Image actuelle:" #: f.mashup.cc:1263 msgid "Cycle through images:" msgstr "Cycle à travers les images:" #: f.mashup.cc:1270 msgid "Scale" msgstr "Échelle" #: f.mashup.cc:1279 msgid "Stacking Order" msgstr "Ordre de Superposition" #: f.mashup.cc:1280 msgid "raise" msgstr "augmenter" #: f.mashup.cc:1281 msgid "lower" msgstr "baisser" #: f.mashup.cc:1284 msgid "Base Transparency" msgstr "Transparence de Base" #: f.mashup.cc:1288 msgid "Var. Transparency" msgstr "Var. Transparence" #: f.mashup.cc:1289 msgid "Paint" msgstr "Peindre" #: f.mashup.cc:1292 msgid "Bend and fine-align" msgstr "Cintrer et aligner " #: f.mashup.cc:1293 msgid "Warp" msgstr "Déformer" #: f.mashup.cc:1302 msgid "Margins" msgstr "Marges" #: f.mashup.cc:1303 msgid "Hard" msgstr "Dur" #: f.mashup.cc:1304 f.repair.cc:5441 msgid "Blend" msgstr "Mélanger" #: f.mashup.cc:1318 msgid "add images to layout" msgstr "ajouter les images au montage" #: f.mashup.cc:1557 msgid "Paint Image Transparencies" msgstr "Peindre les Transparences de l'Image" #: f.mashup.cc:1575 msgid "Gradual" msgstr "Progressif" #: f.mashup.cc:1580 fotoxx.h:1148 msgid "Power" msgstr "Puissance" #: f.mashup.cc:1814 msgid "Pull on the image with the mouse." msgstr "Tirer l'image avec la souris." #: f.mashup.cc:1830 msgid "Warp Image" msgstr "Déformer l'Image" #: f.mashup.cc:2263 #, c-format msgid "exceeded %d images" msgstr "Excède %d images" #: f.mashup.cc:2405 msgid "Enter text, [Add] to layout, edit properties." msgstr "Saisir du texte [Ajouter] au montage, éditer les propriétés." #: f.mashup.cc:2485 msgid "Text File:" msgstr "Fichier Texte:" #: f.mashup.cc:2489 msgid "add entered text to layout" msgstr "ajouter le texte saisi au montage" #: f.mashup.cc:2618 msgid "click position to add text" msgstr "Cliquer l'emplacement ou ajouter le texte" #: f.mashup.cc:2624 #, c-format msgid "exceeded %d text entries" msgstr "excède %d entrées de texte" #: f.mashup.cc:2747 msgid "Set line properties, [Add] to layout, edit." msgstr "Définir les propriétés de la ligne, [Ajouter] au montage, éditer" #: f.mashup.cc:2771 msgid "Edit Line/Arrow" msgstr "Éditer la Ligne/Flèche" #: f.mashup.cc:2826 msgid "add line/arrow to layout" msgstr "ajouter ligne/flèche au montage" #: f.mashup.cc:2919 msgid "click position to add line" msgstr "Cliquer l'emplacement pour ajouter une ligne" #: f.mashup.cc:2925 #, c-format msgid "exceeded %d line entries" msgstr "excède %d entrées de ligne" #: f.meta.cc:180 f.widgets.cc:669 msgid "View Metadata" msgstr "Afficher les Métadonnées" #: f.meta.cc:430 f.meta.cc:1807 f.widgets.cc:412 f.widgets.cc:670 msgid "Edit Metadata" msgstr "Éditer les Métadonnées" #: f.meta.cc:433 f.meta.cc:3819 msgid "save metadata to file" msgstr "enregistrer les métadonnées dans un fichier" #: f.meta.cc:442 msgid "Image Date" msgstr "Date de l'Image" #: f.meta.cc:445 msgid "Time" msgstr "Heure" #: f.meta.cc:451 msgid "Rating (stars):" msgstr "Évaluation (étoiles)" #: f.meta.cc:463 msgid "Caption" msgstr "Légende" #: f.meta.cc:469 msgid "Comments" msgstr "Commentaires" #: f.meta.cc:477 msgid "Enter New Tag" msgstr "Saisir une Nouvelle Balise" #: f.meta.cc:484 f.meta.cc:5003 msgid "Matching Tags" msgstr "Balises Correspondantes" #: f.meta.cc:492 msgid "Image Tags" msgstr "Balises de l'Image" #: f.meta.cc:498 msgid "Recent Tags" msgstr "Balises Récentes" #: f.meta.cc:507 f.meta.cc:2029 f.meta.cc:5010 msgid "Defined Tags Category" msgstr "Catégorie des Balises Définies" #: f.meta.cc:898 msgid "date format is YYYY-MM-DD" msgstr "le format de date est AAAA-MM-JJ" #: f.meta.cc:902 msgid "date is invalid" msgstr "Date incorrecte" #: f.meta.cc:936 msgid "time format is HH:MM [:SS]" msgstr "Le format de l'heure est HH:MM [:SS]" #: f.meta.cc:940 msgid "time is invalid" msgstr "heure incorrecte" #: f.meta.cc:1025 fotoxx.h:1133 msgid "Manage Tags" msgstr "Gestion des Balises" #: f.meta.cc:1025 msgid "orphan tags" msgstr "balises orphelines" #: f.meta.cc:1029 msgid "category" msgstr "catégorie" #: f.meta.cc:1032 msgid "tag" msgstr "balise" #: f.meta.cc:1039 msgid "Defined Tags:" msgstr "Balises Définies:" #: f.meta.cc:1811 f.meta.cc:2474 msgid "key name" msgstr "nom de la touche" #: f.meta.cc:1813 f.meta.cc:2475 msgid "key value" msgstr "valeur de la touche" #: f.meta.cc:1900 f.widgets.cc:414 msgid "Delete Metadata" msgstr "Supprimer les Métadonnées" #: f.meta.cc:1902 fotoxx.h:1082 msgid "All" msgstr "Toutes" #: f.meta.cc:1903 msgid "One Key:" msgstr "Un Mot-Clé: " #: f.meta.cc:2006 f.widgets.cc:415 f.widgets.cc:540 msgid "Batch Add/Remove Tags" msgstr "Ajouter/Retirer des Balises par Lots" #: f.meta.cc:2017 msgid "tags to add" msgstr "balises à ajouter" #: f.meta.cc:2018 msgid "tags to remove" msgstr "balises à retirer" #: f.meta.cc:2102 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " trop de balises" #: f.meta.cc:2217 msgid "specify files and tags" msgstr "spécifier fichiers et balises" #: f.meta.cc:2277 msgid "tag names file" msgstr "fichier des noms de balises" #: f.meta.cc:2349 #, c-format msgid "" "%d tags to rename \n" "in %d image files. \n" "Proceed?" msgstr "" "%d balises à renommer \n" "dans %d fichiers images. \n" "Procéder?" #: f.meta.cc:2461 msgid "Batch Metadata" msgstr "Traitement des Métadonnées par Lots" #: f.meta.cc:2468 msgid "Short List" msgstr "" #: f.meta.cc:2469 msgid "Full List" msgstr "" #: f.meta.cc:2511 msgid "enter key names" msgstr "saisir le noms des clés" #: f.meta.cc:2517 fotoxx.h:1141 msgid "no files selected" msgstr "aucun fichier sélectionné" #: f.meta.cc:2643 msgid "" "The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names" msgstr "" #: f.meta.cc:3276 f.meta.cc:4011 f.meta.cc:4277 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "latitude/longitude incorrectes: %s %s" #: f.meta.cc:3320 f.meta.cc:3682 f.meta.cc:4486 f.meta.cc:4894 msgid "-noindex in use, disabled" msgstr "pas d'index utilisé, désactivé" #: f.meta.cc:3336 msgid "choose map file" msgstr "Choisir le fichier de la carte" #: f.meta.cc:3444 msgid "" "fotoxx-maps package not installed \n" "(see http://kornelix.com/packages and /tarballs)" msgstr "" "paquet fotoxx-maps non installé \n" "(voir http://kornelix.com/packages et /tarballs)" #: f.meta.cc:3449 #, c-format msgid "map file %s is missing" msgstr "Fichier de carte %s manquant" #: f.meta.cc:3453 #, c-format msgid "" "map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f" msgstr "" "Données de longitude/latitude de la carte incohérentes \n" " %.3f %.3f %.3f %.3f" #: f.meta.cc:3731 msgid "No matching images found" msgstr "Aucun résultat pour cette recherche" # ?? #: f.meta.cc:3771 msgid "search range (km)" msgstr "fourchette de recherche (km)" #: f.meta.cc:3804 f.widgets.cc:418 f.widgets.cc:671 msgid "Edit Geotags" msgstr "Éditer les Balises de Géolocalisation" #: f.meta.cc:3805 msgid "Geocoding web service courtesy of" msgstr "Service Web de Géo-codage avec l'aimable autorisation de" #: f.meta.cc:3821 f.meta.cc:4060 f.meta.cc:5783 msgid "city" msgstr "ville" #: f.meta.cc:3824 f.meta.cc:4063 f.meta.cc:5786 msgid "country" msgstr "pays" #: f.meta.cc:3906 f.meta.cc:4194 msgid "city not found" msgstr "Ville introuvable" #: f.meta.cc:4027 f.widgets.cc:419 f.widgets.cc:543 msgid "Batch Add Geotags" msgstr "Ajouter des Balises de Géolocalisation par Lots" #: f.meta.cc:4256 msgid "" "data is incomplete \n" " proceed?" msgstr "" "données incomplètes \n" " poursuivre?" #: f.meta.cc:4313 msgid "choose city" msgstr "choisir une ville" #: f.meta.cc:4400 msgid "not found" msgstr "introuvable" #: f.meta.cc:4401 msgid "city and country required" msgstr "ville et pays requis" #: f.meta.cc:4501 msgid "Report Geotag Groups" msgstr "Faire le Rapport des Groupes de Balises de Géolocalisation" #: f.meta.cc:4502 msgid "Group by country" msgstr "Grouper par pays" #: f.meta.cc:4503 msgid "Group by country/city" msgstr "Grouper par pays/ville" #: f.meta.cc:4504 msgid "Group by country/city/date" msgstr "Grouper par pays/ville/date" #: f.meta.cc:4507 msgid "Combine within" msgstr "Combiner avec" #: f.meta.cc:4509 msgid "days" msgstr "Jours" #: f.meta.cc:4625 msgid "geotag groups" msgstr "groupes de balises de géolocalisation" #: f.meta.cc:4931 msgid "Search Image Metadata" msgstr "Rechercher les Métadonnées de l'Image" #: f.meta.cc:4935 msgid "images to search:" msgstr "images à rechercher:" #: f.meta.cc:4936 msgid "all" msgstr "toutes" #: f.meta.cc:4937 msgid "current set only" msgstr "ensemble actuel uniquement" #: f.meta.cc:4940 msgid "matching images:" msgstr "images correspondantes:" #: f.meta.cc:4941 msgid "new set" msgstr "nouvel ensemble" #: f.meta.cc:4942 msgid "add to set" msgstr "ajouter à l'ensemble" #: f.meta.cc:4943 msgid "remove" msgstr "retirer" #: f.meta.cc:4946 msgid "report type:" msgstr "type de rapport:" #: f.meta.cc:4947 msgid "gallery" msgstr "galerie" #: f.meta.cc:4948 fotoxx-15.11.cc:791 msgid "metadata" msgstr "métadonnées" #: f.meta.cc:4954 msgid "date range" msgstr "fourchette de dates" #: f.meta.cc:4955 msgid "stars range" msgstr "plage d'étoiles" #: f.meta.cc:4956 msgid "search tags" msgstr "rechercher les balises" #: f.meta.cc:4957 msgid "search text" msgstr "Rechercher le texte" #: f.meta.cc:4958 msgid "search files" msgstr "Rechercher les fichiers" #: f.meta.cc:4963 msgid "(yyyymmdd)" msgstr "(aaaammjj)" #: f.meta.cc:4968 msgid "last version only" msgstr "Dernière version uniquement" #: f.meta.cc:4970 msgid "all/any" msgstr "toutes/quelconques" #: f.meta.cc:4989 msgid "other criteria" msgstr "autre critère" #: f.meta.cc:4993 msgid "other" msgstr "autre" #: f.meta.cc:4999 msgid "Enter Search Tag" msgstr "Saisir la Balise de Recherche" #: f.meta.cc:5294 #, c-format msgid "not a defined tag: %s" msgstr "balise non définie: %s" #: f.meta.cc:5325 msgid "" "to remove images from current set, \n" "search current set" msgstr "" "pour retirer des images de l'ensemble actuel, \n" "rechercher l'ensemble actuel" #: f.meta.cc:5332 msgid "" "to add images to current set, \n" "search all images" msgstr "" "pour ajouter des images à l'ensemble actuel, \n" "rechercher toutes les images" #: f.meta.cc:5376 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "Rechercher les dates incohérentes \n" " %s %s" #: f.meta.cc:5394 msgid "stars range not reasonable" msgstr "fourchette d'étoiles incohérente" #: f.meta.cc:5639 #, c-format msgid "images added: %d removed: %d new count: %d" msgstr "images ajoutées: %d retirées: %d nouveau nombre: %d" #: f.meta.cc:5642 msgid "no changes made" msgstr "aucune modification apportée" #: f.meta.cc:5780 msgid "Add Geotags Search Criteria" msgstr "Ajouter un Critère de Recherche de Balises de Géolocalisation" #: f.meta.cc:5799 msgid "range (km)" msgstr "fourchette (km)" #: f.meta.cc:5892 msgid "error in latitude/longitude/range" msgstr "erreur de latitude/longitude/fourchette" #: f.meta.cc:5956 msgid "" "These items are always reported: \n" "date, stars, tags, caption, comment" msgstr "" "Ces éléments sont toujours rapportés: \n" "date, étoiles, balises, légende, commentaire" #: f.meta.cc:5980 msgid "Additional Items for Report" msgstr "Éléments Supplémentaires du Rapport" #: f.meta.cc:5986 msgid "Keyword" msgstr "Mot-clé" #: f.meta.cc:5993 msgid "Match Criteria" msgstr "Critère de Correspondance" #: f.meta.cc:6553 msgid "image index is missing" msgstr "index d'images manquant" #: f.repair.cc:1041 msgid "Apply repeatedly while watching the image." msgstr "Appliquer à plusieurs reprises en observant l'image." #: f.repair.cc:1042 msgid "Measure" msgstr "Mesurer" #: f.repair.cc:1078 msgid "Noise Reduction" msgstr "Réduction de Bruit" #: f.repair.cc:1099 msgid "Flatten 1" msgstr "" #: f.repair.cc:1100 msgid "Flatten 2" msgstr "" #: f.repair.cc:1101 msgid "Median" msgstr "" #: f.repair.cc:1121 msgid "dark areas" msgstr "zones sombres" #: f.repair.cc:1123 msgid "all areas" msgstr "Toutes les zones" #: f.repair.cc:1246 msgid "Measure Noise" msgstr "Mesurer le Bruit" #: f.repair.cc:1247 msgid "Click on a monotone image area." msgstr "Cliquer sur une zone monotone de l'image." #: f.repair.cc:2177 msgid "" "1. Drag mouse to select. \n" "2. Erase. 3. Repeat. " msgstr "" "1. Glisser la souris pour sélectionner. \n" "2. Effacer. 3. Répéter." #: f.repair.cc:2199 f.widgets.cc:457 msgid "Smart Erase" msgstr "Effaçage Intelligent" #: f.repair.cc:2204 fotoxx.h:1152 msgid "Radius" msgstr "Rayon" #: f.repair.cc:2206 f.widgets.cc:455 msgid "Blur" msgstr "Flou" #: f.repair.cc:2209 msgid "New Area" msgstr "Nouvelle Zone" #: f.repair.cc:2558 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 n°1 : \n" " - Clic-gauche sur l'œil rouge à assombrir. \n" "Méthode n°2 : \n" " - Circonscrire l’œil rouge en tirant le pointeur vers le bas puis à " "droite. \n" " - Clic-droit sur l'œil rouge pour annuler." #: f.repair.cc:2574 msgid "Red Eye Reduction" msgstr "Réduction des Yeux Rouges" #: f.repair.cc:3038 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 "" "Touche Maj + clic gauche: choisir une couleur ou un endroit de l’image \n" "Cliquer gauche ou glisser: peindre une couleur ou copier l'image \n" "Cliquer droit ou glisser: retirer la couleur ou l'image" #: f.repair.cc:3070 f.widgets.cc:459 msgid "Paint/Clone" msgstr "Peinture/Duplication" #: f.repair.cc:3074 msgid "paint color" msgstr "couleur de peinture" #: f.repair.cc:3077 msgid "copy from image" msgstr "copier à partir de l'image" #: f.repair.cc:3084 f.repair.cc:3641 msgid "paintbrush radius" msgstr "rayon de la brosse" #: f.repair.cc:3085 msgid "transparency center" msgstr "centre de transparence" #: f.repair.cc:3086 msgid "transparency edge" msgstr "bord de transparence" #: f.repair.cc:3093 f.repair.cc:3648 msgid "gradual paint" msgstr "peinture progressive" #: f.repair.cc:3100 #, c-format msgid "Undo Memory %d%c" msgstr "Mémoire d'Annulation %d%c" #: f.repair.cc:3436 msgid "" "Undo memory limit has been reached. \n" "Save work with [done], then resume painting." msgstr "" "La limite de mémoire d'annulation a été atteinte. \n" "Sauvegardez votre travail en cliquant sur [Terminé], \n" "puis reprenez la peinture." #: f.repair.cc:3601 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "glissement gauche: ajouter de la transparence \n" "glissement droit: ajouter de l'opacité" #: f.repair.cc:3633 f.widgets.cc:460 msgid "Paint Transparency" msgstr "Peindre la Transparence" #: f.repair.cc:3642 msgid "strength center" msgstr "centre de force" #: f.repair.cc:3643 msgid "strength edge" msgstr "bord de force" #: f.repair.cc:3858 f.widgets.cc:461 msgid "Color Mode" msgstr "Mode de Couleur" #: f.repair.cc:3862 msgid "black/white positive" msgstr "noir/blanc positif" #: f.repair.cc:3863 msgid "black/white negative" msgstr "noir/blanc négatif" #: f.repair.cc:3864 msgid "color positive" msgstr "couleur positive" #: f.repair.cc:3865 msgid "color negative" msgstr "couleur négative" #: f.repair.cc:3866 msgid "sepia" msgstr "sépia" #: f.repair.cc:4046 f.widgets.cc:462 msgid "Shift Colors" msgstr "Changement de Couleurs " #: f.repair.cc:4317 msgid "+Brightness" msgstr "+Luminosité" #: f.repair.cc:4318 msgid "+Red -Cyan" msgstr "+Rouge -Cyan" #: f.repair.cc:4319 msgid "+Green -Magenta" msgstr "+Vert -Magenta" #: f.repair.cc:4320 msgid "+Blue -Yellow" msgstr "+Bleu -Jaune" #: f.repair.cc:4326 fotoxx.h:1154 msgid "Red" msgstr "Rouge" #: f.repair.cc:4327 fotoxx.h:1120 msgid "Green" msgstr "Vert" #: f.repair.cc:4328 fotoxx.h:1089 msgid "Blue" msgstr "Bleu" #: f.repair.cc:4623 msgid "Color to change" msgstr "" #: f.repair.cc:4625 msgid "shift+click on image to select color" msgstr "" #: f.repair.cc:4638 msgid "Color Hue" msgstr "Nuance de couleur" #: f.repair.cc:4639 msgid "Saturation" msgstr "Saturation" #: f.repair.cc:4640 msgid "Lightness" msgstr "Clarté" #: f.repair.cc:4641 msgid "Color Match" msgstr "" #: f.repair.cc:4642 msgid "Adjustment" msgstr "Ajustements" #: f.repair.cc:5028 msgid "Ramp brightness across image" msgstr "Corriger la luminosité à travers l'image" #: f.repair.cc:5352 f.tools.cc:2048 msgid "Click image to select pixels." msgstr "Cliquer l'image pour sélectionner les pixels." #: f.repair.cc:5383 f.widgets.cc:466 msgid "Color Ramp" msgstr "Rampe de Couleurs" #: f.repair.cc:5394 msgid "Metric:" msgstr "Métrique:" #: f.repair.cc:5817 msgid "Color Match Images" msgstr "Images de Couleurs Correspondantes" #: f.repair.cc:5843 msgid "mouse radius for color sample" msgstr "rayon du pointeur d'échantillonnage de couleur" #: f.repair.cc:5846 msgid "image for source color" msgstr "image pour la source de couleur" #: f.repair.cc:5848 msgid "click on image to get source color" msgstr "cliquer sur l'image pour obtenir la source de couleur" #: f.repair.cc:5851 msgid "image to set matching color" msgstr "image pour mettre la couleur correspondante" #: f.repair.cc:5853 msgid "click on image to set matching color" msgstr "cliquer sur l'image pour mettre la couleur correspondante" #: f.repair.cc:5914 msgid "select source image color first" msgstr "sélectionner d'abord la couleur de l'image source" #: f.repair.cc:6099 msgid "Change Color Profile" msgstr "Changer de Profil de Couleur" #: f.repair.cc:6103 msgid "input profile" msgstr "fichier d'entrée" #: f.repair.cc:6107 msgid "output profile" msgstr "fichier de sortie" #: f.repair.cc:6123 msgid "color profile" msgstr "profil de Couleur" #: f.repair.cc:6169 f.repair.cc:6175 #, c-format msgid "unknown cms profile %s" msgstr "profile cms inconnu %s" #: f.repair.cc:6307 f.widgets.cc:469 msgid "Remove Dust" msgstr "Anti-Poussières" #: f.repair.cc:6311 msgid "spot size limit" msgstr "taille limite des grains de poussière" #: f.repair.cc:6314 msgid "max. brightness" msgstr "luminosité max." #: f.repair.cc:6317 msgid "min. contrast" msgstr "contraste min." #: f.repair.cc:7098 msgid "" " Adjust each RGB color to minimize \n" " color fringes at the image extremes. " msgstr "" " Ajuster chaque couleur RGB pour diminuer \n" " les franges de couleurs aux bords de l'image." #: f.repair.cc:7121 f.widgets.cc:471 msgid "Color Fringes" msgstr "Bords Colorés" #: f.repair.cc:7320 f.widgets.cc:472 msgid "Stuck Pixels" msgstr "Pixels Bloqués" #: f.repair.cc:7326 msgid "pixel group" msgstr "groupe de pixels" #: f.repair.cc:7334 f.repair.cc:7402 msgid "stuck pixels:" msgstr "pixels bloqués:" #: f.repair.cc:7430 msgid "Load Stuck Pixels" msgstr "Charger les Pixels Bloqués" #: f.repair.cc:7432 msgid "File:" msgstr "Fichier:" #: f.repair.cc:7457 f.repair.cc:7514 msgid "Stuck Pixels file" msgstr "fichier des Pixels Bloqués" #: f.repair.cc:7494 msgid "file format error" msgstr "erreur de format de fichier" #: f.tools.cc:70 msgid "" "Select directories containing image files \n" "(subdirectories are included automatically)." msgstr "" "Sélectionner les répertoires contenant les fichiers images \n" "(les sous-répertoires sont inclus automatiquement)." #: f.tools.cc:72 msgid "Select to add, click on X to delete." msgstr "Sélectionner pour ajouter, click sur X pour supprimer." #: f.tools.cc:73 msgid "Select directory for thumbnails." msgstr "Sélectionner un répertoire pour les vignettes." #: f.tools.cc:74 msgid "" "Index function terminated. Fotoxx will close. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Fonction d'index terminée. Fotoxx va se fermer. \n" "L'indexation est requise pour les fonctions de recherche et de carte \n" "ainsi que pour accéder aux pages de la galerie plus rapidement." #: f.tools.cc:106 f.widgets.cc:513 msgid "Index Image Files" msgstr "Index des Fichiers Images" #: f.tools.cc:250 msgid "Choose top image directories" msgstr "Sélectionner le répertoire d'images racine" #: f.tools.cc:251 msgid "Choose thumbnail directory" msgstr "Choisir le répertoire des vignettes" #: f.tools.cc:324 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 "" "Fichier index des images introuvable.\n" "Un fichier index des images va être créé.\n" "Vos fichiers images ne seront pas modifiés.\n" "Cette opération peut demander un certain temps si\n" "vous avez plusieurs milliers de fichiers images." #: f.tools.cc:330 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" "Répertoire non valide: \n" " %s \n" "Veuillez le retirer." #: f.tools.cc:333 #, c-format msgid "" "Thumbnails directory: \n" " %s \n" "must be named .../thumbnails" msgstr "" "Répertoire des vignettes \n" " %s \n" "Doit être nommé .../thumbnails" #: f.tools.cc:336 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" "Répertoire doublon: \n" " %s \n" "Veuillez le retirer." #: f.tools.cc:385 msgid "no thumbnails directory defined" msgstr "répertoire des vignettes non défini" #: f.tools.cc:735 msgid "COMPLETED" msgstr "COMPLÉTÉ" #: f.tools.cc:776 msgid "Indexing is required for first-time startup." msgstr "Indexation requise au premier lancement." #: f.tools.cc:838 msgid "Recent Files Gallery" msgstr "Galerie des Fichiers Récents" #: f.tools.cc:839 msgid "Newest Files Gallery" msgstr "Galerie des Derniers Fichiers" #: f.tools.cc:840 msgid "Previous Gallery" msgstr "Galerie Précédente" #: f.tools.cc:841 msgid "Previous Image" msgstr "Image Précédente" #: f.tools.cc:842 msgid "Blank Window" msgstr "Fenêtre Vierge" #: f.tools.cc:843 msgid "Directory" msgstr "Répertoire" #: f.tools.cc:844 msgid "Image File" msgstr "Fichier Image" #: f.tools.cc:887 f.widgets.cc:514 msgid "User Options" msgstr "Options Utilisateur" #: f.tools.cc:890 msgid "Startup Display" msgstr "Afficher au démarrage" #: f.tools.cc:901 msgid "Menu Style" msgstr "Style de Menu" #: f.tools.cc:902 msgid "Icons" msgstr "Icônes" #: f.tools.cc:903 msgid "Icons + Text" msgstr "Icônes + Texte" #: f.tools.cc:905 msgid "Icon size" msgstr "Taille d’icône" #: f.tools.cc:909 msgid "Dialog font and size" msgstr "Police et taille de boite de dialogue" #: f.tools.cc:914 msgid "Image Pan/scroll:" msgstr "Défilement/Panorama Image" #: f.tools.cc:917 msgid "Zooms for 2x" msgstr "Zooms de 2x" #: f.tools.cc:926 msgid "JPEG save quality" msgstr "Qualité d'enregistrement JPEG" #: f.tools.cc:933 msgid "Curve node capture distance" msgstr "" #: f.tools.cc:937 msgid "show hidden directories in gallery view" msgstr "affichier les répertoires cachés dans la galerie" #: f.tools.cc:940 msgid "prev/next shows last file version only" msgstr "préc/suiv n'affiche que la dernière version de fichier" #: f.tools.cc:943 msgid "RAW command" msgstr "commande RAW" #: f.tools.cc:947 msgid "RAW file types" msgstr "type de fichier RAW" #: f.tools.cc:1029 msgid "Delete present thumbnails to make effective" msgstr "Supprimer les vignettes pour rendre effectif" #: f.tools.cc:1051 msgid "Select startup directory" msgstr "Sélectionner le répertoire de démarrage" #: f.tools.cc:1058 msgid "Select startup image file" msgstr "Sélectionner le fichier image de démarrage" #: f.tools.cc:1096 msgid "startup directory is invalid" msgstr "répertoire de démarrage incorrect" #: f.tools.cc:1107 msgid "startup file is invalid" msgstr "fichier de démarrage incorrect" #: f.tools.cc:1247 msgid "Edit KB Shortcuts" msgstr "Éditer les Raccourcis Clavier" #: f.tools.cc:1252 msgid "shortcut key:" msgstr "touche de raccourci:" #: f.tools.cc:1253 msgid "(enter key)" msgstr "(touche entrée)" #: f.tools.cc:1379 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Réservé, inutilisable" #: f.tools.cc:1533 msgid "unable to save KB-shortcuts file" msgstr "impossible d'enregistrer le fichier des Raccourcis Clavier" #: f.tools.cc:1837 f.widgets.cc:517 msgid "Grid Lines" msgstr "Guides de la Grille" #: f.tools.cc:1846 msgid "x-spacing" msgstr "x-espacement" #: f.tools.cc:1847 msgid "x-count" msgstr "x-nombre" #: f.tools.cc:1848 msgid "x-enable" msgstr "x-activé" #: f.tools.cc:1854 msgid "y-spacing" msgstr "y-espacement" #: f.tools.cc:1855 msgid "y-count" msgstr "y-nombre" #: f.tools.cc:1856 msgid "y-enable" msgstr "y-activé" #: f.tools.cc:1976 f.tools.cc:1981 f.widgets.cc:518 msgid "Line Color" msgstr "Couleur de Ligne" #: f.tools.cc:1978 msgid "Area Color" msgstr "Couleur de Zone" #: f.tools.cc:2083 f.widgets.cc:519 msgid "Show RGB" msgstr "Montrer les couleurs RVB" #: f.tools.cc:2375 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key M to toggle dialog." msgstr "" "Glisser le curseur sur l'image. \n" "Clic gauche pour annuler. \n" "Touche M pour changer de dialogue." #: f.tools.cc:2401 f.widgets.cc:520 msgid "Magnify Image" msgstr "Agrandir l'image" #: f.tools.cc:2410 msgid "X-size" msgstr "X-taille" #: f.tools.cc:2655 msgid "Darkest and Brightest Pixels" msgstr "Pixels les plus foncés et clairs" #: f.tools.cc:2678 msgid "Dark Limit" msgstr "Limite foncé" #: f.tools.cc:2679 msgid "Bright Limit" msgstr "Limite clair" #: f.tools.cc:2774 msgid "" "Brightness should show a gradual ramp \n" "extending all the way to the edges." msgstr "" "La luminosité devrait être progressive, \n" "s'étendant régulièrement d'un bord à l'autre." #: f.tools.cc:2870 f.widgets.cc:523 msgid "Monitor Gamma" msgstr "Gamma de l'écran" #: f.tools.cc:2930 msgid "Available Translations" msgstr "Traductions disponibles" #: f.tools.cc:2934 msgid "Set Language" msgstr "Définir la langue" #: f.tools.cc:3071 f.widgets.cc:526 msgid "Calibrate Printer" msgstr "Calibrer l'Imprimante" #: f.tools.cc:3096 msgid "print color chart" msgstr "imprimer la charte des couleurs" #: f.tools.cc:3097 msgid "scan and save color chart" msgstr "Scanner et enregistrer la charte des couleurs" #: f.tools.cc:3098 msgid "align and trim color chart" msgstr "aligner et rogner le nuancier" #: f.tools.cc:3099 msgid "open and process color chart" msgstr "ouvrir et traiter le nuancier" #: f.tools.cc:3100 msgid "print image with revised colors" msgstr "imprimer l'image avec les couleurs révisées" #: f.tools.cc:3264 #, c-format msgid "" "Scan the printed color chart. \n" "Save in %s/" msgstr "" "Scanner le nuancier imprimé. \n" "Enregistrer dans %s/" #: f.tools.cc:3278 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation caused by the scanner. \n" "Trim off all margins. Be careful not to cut off \n" "any color tile edges or leave any white margins." msgstr "" "Ouvrir et éditer le fichier-nuancier scanné. \n" "Retirer toute rotation ou gauchissement causés par le scanner. \n" "Rogner toutes les marges. Faites attention de ne pas couper \n" "n'importe quels bords colorés ou de laisser de marges blanches." #: f.tools.cc:3310 msgid "Open the trimmed color chart file" msgstr "Ouvrir le fichier-nuancier réduit" #: f.tools.cc:3443 msgid "" "Set the name for the output calibration file \n" "[your calibration name].dat" msgstr "" #: f.tools.cc:3483 msgid "Color map file to use" msgstr "Fichier de carte de couleurs à utiliser" #: f.tools.cc:3500 msgid "Select the image file to print." msgstr "Sélectionner le fichier image à imprimer." #: f.tools.cc:3565 msgid "converting colors..." msgstr "" #: f.tools.cc:3665 msgid "Image colors are converted for printing." msgstr "Les couleurs de l'image sont converties pour l'impresssion." #: f.widgets.cc:111 msgid "Album" msgstr "Album" #: f.widgets.cc:113 msgid "TOP" msgstr "TOP" #: f.widgets.cc:165 msgid "Current Image File (key F)" msgstr "Fichier Image Actuel (touche F)" #: f.widgets.cc:166 msgid "Thumbnail Gallery (key G)" msgstr "Galerie des vignettes (touche G)" #: f.widgets.cc:167 msgid "World Maps (key W)" msgstr "Mappemondes (touche W)" #: f.widgets.cc:170 msgid "Favorite Functions" msgstr "Fonctions favorites" #: f.widgets.cc:171 msgid "File: Open, RAW, Rename, Trash, Print" msgstr "Fichier: Ouvrir, RAW, Renommer, Mettre à la Corbeille, Imprimer" #: f.widgets.cc:172 msgid "Save modified image file to disk" msgstr "Enregistrer l'image modifiée sur le disque" #: f.widgets.cc:173 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:174 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "" "Métadonnées: Légendes, Balises, Évaluations, Balises de Géolocalisation, " "Recherche..." #: f.widgets.cc:175 msgid "Areas: Select areas to edit, copy and paste" msgstr "Zones: Sélectionner les zones à éditer, copier et coller" #: f.widgets.cc:176 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "" "Éditer: Rogner, Pivoter, Redimensionner, Luminosité, Contraste, Texte..." #: f.widgets.cc:177 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "" "Réparer: Netteté, Bruit, Yeux rouges, Couleur, Peinture, Duplication..." #: f.widgets.cc:178 msgid "Bend: Fix Perspective, Bend/Warp image ..." msgstr "Cintrer: Fixer la perspective, Cintrer/Déformer l'image..." #: f.widgets.cc:179 msgid "Effects: Special Effects, Arty Transforms" msgstr "Effets: Effets Spéciaux, Transformations Style Artiste" #: f.widgets.cc:180 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "Combiner: HDR, HDF, Panorama, Superposition, Pêle-Mêle" #: f.widgets.cc:181 msgid "" "Undo or Redo one edit (left/right mouse click) \n" " hold key A to include all edits" msgstr "" "Annuler ou Rétablir une opération (clic gauche/droit) \n" " maintenir la touche A enfoncée pour englober toutes les retouches" #: f.widgets.cc:183 msgid "Tools: Index, Options, Shortcuts, Magnify ..." msgstr "Outils: Index, Options, Raccourcis, Agrandissement..." #: f.widgets.cc:184 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "" "Aide: Démarrage rapide, Manuel de l'Utilisateur, Changements récents..." #: f.widgets.cc:187 msgid "Set gallery from current image file" msgstr "" "Afficher le répertoire contenant le fichier image actuel dans la galerie" #: f.widgets.cc:188 msgid "Albums: Manage Albums, Slide Show, Desktop Wallpaper" msgstr "" #: f.widgets.cc:189 msgid "go to bookmarked image" msgstr "aller à l'image marquée du signet" #: f.widgets.cc:190 msgid "increase thumbnail size" msgstr "augmenter la taille des vignettes" #: f.widgets.cc:191 msgid "reduce thumbnail size" msgstr "diminuer la taille des vignettes" #: f.widgets.cc:192 msgid "change sort order" msgstr "changer l'ordre de tri" #: f.widgets.cc:193 msgid "jump to beginning (top)" msgstr "aller au début (racine)" #: f.widgets.cc:194 msgid "jump to end (bottom)" msgstr "aller à la fin (bas)" #: f.widgets.cc:195 msgid "previous page" msgstr "page précédente" #: f.widgets.cc:196 msgid "next page" msgstr "page suivante" #: f.widgets.cc:197 msgid "slow scroll" msgstr "défilement doux" #: f.widgets.cc:198 msgid "Batch conversion, metadata, RAW processing" msgstr "Conversion, traitement des métadonnées et RAW par lots" #: f.widgets.cc:201 msgid "Choose a map for image locations" msgstr "Sélectionner une carte pour les lieux de l'image" #: f.widgets.cc:202 msgid "Set image search radius for map click" msgstr "Définir le rayon de recherche de l'image pour clic sur carte" #: f.widgets.cc:205 msgid "Open another window" msgstr "Ouvrir une nouvelle fenêtre" #: f.widgets.cc:206 msgid "Open a new image file" msgstr "Ouvrir un nouveau fichier image" #: f.widgets.cc:207 msgid "Open the previously seen file" msgstr "Ouvrir l'image affichée précédemment" #: f.widgets.cc:208 msgid "Open a recently seen file" msgstr "Ouvrir un fichier récemment affiché" #: f.widgets.cc:209 msgid "Open a newly added file" msgstr "Ouvrir un fichier ajouté dernièrement" #: f.widgets.cc:210 f.widgets.cc:211 msgid "Open and edit a camera RAW file" msgstr "Ouvrir et éditer un fichier image RAW" #: f.widgets.cc:212 msgid "Change the image file name" msgstr "Changer le nom du fichier image" #: f.widgets.cc:213 msgid "Create a blank image" msgstr "Créer une image vierge" #: f.widgets.cc:214 msgid "Move image file to Trash" msgstr "Déplacer l'image vers la Corbeille" #: f.widgets.cc:215 msgid "Permanently delete image file" msgstr "Supprimer définitivement le fichier image" #: f.widgets.cc:216 msgid "Print the current image" msgstr "Imprimer l'image actuelle" #: f.widgets.cc:217 msgid "Print current image with adjusted colors" msgstr "" #: f.widgets.cc:218 f.widgets.cc:406 msgid "Quit Fotoxx" msgstr "Quitter Fotoxx" #: f.widgets.cc:221 msgid "List a few key metadata items" msgstr "Lister quelques éléments de mots-clés de métadonnées" #: f.widgets.cc:222 msgid "List all metadata items" msgstr "Lister tous les éléments des métadonnées" #: f.widgets.cc:223 msgid "(Toggle) show captions and comments" msgstr "(Basculer) afficher légendes et commentaires" #: f.widgets.cc:224 msgid "Edit image tags/caption/rating ..." msgstr "Éditer les balises/légende/évaluation de l'image..." #: f.widgets.cc:225 msgid "Edit any image metadata" msgstr "Éditer n'importe quelle métadonnée de l'image" #: f.widgets.cc:226 msgid "Remove all metadata from an image" msgstr "Retirer toutes les métadonnées d'une image" #: f.widgets.cc:227 msgid "Add/remove tags for multiple images" msgstr "Ajouter/retirer les balises par lots" #: f.widgets.cc:228 msgid "Convert tag names for all images" msgstr "Convertir les noms de balises pour toutes les images" #: f.widgets.cc:229 msgid "Add/change/delete metadata for multiple images" msgstr "Ajouter/modifier/supprimer les métadonnées de plusieurs images" #: f.widgets.cc:230 msgid "Edit image location and geotags" msgstr "Éditer le lieu et les balises de géolocalisation de l'image" #: f.widgets.cc:231 msgid "Add/revise geotags for multiple images" msgstr "Ajouter/réviser les balises de géolocalisation de plusieurs imgaes" #: f.widgets.cc:232 msgid "Find all images for a location [date]" msgstr "Rechercher toutes les images pour un lieu géographique [date]" #: f.widgets.cc:233 msgid "Find images meeting select criteria" msgstr "Rechercher les images correspondant au critère de sélection" #: f.widgets.cc:236 msgid "Select object or area for editing" msgstr "Sélectionner un objet ou une zone de travail" #: f.widgets.cc:237 msgid "Show (outline) existing area" msgstr "Montrer la zone existante (contours)" #: f.widgets.cc:238 msgid "Hide existing area" msgstr "Masquer la zone existante" #: f.widgets.cc:239 msgid "Enable area for editing" msgstr "Activer la zone de travail" #: f.widgets.cc:240 msgid "Disable area for editing" msgstr "Désactiver la zone de travail" #: f.widgets.cc:241 msgid "Reverse existing area" msgstr "Inverser la zone existante" #: f.widgets.cc:242 msgid "Erase existing area" msgstr "Effacer la zone existante" #: f.widgets.cc:243 msgid "Copy area for later pasting into image" msgstr "Copier la zone pour un collage ultérieur dans l'image" #: f.widgets.cc:244 msgid "Paste previously copied area into image" msgstr "Coller la zone précédemment copiée dans l'image" #: f.widgets.cc:245 msgid "Open a file and paste as area into image" msgstr "Ouvrir un fichier et le coller comme zone dans l'image" #: f.widgets.cc:246 msgid "Save area to a file with transparency" msgstr "Enregistrer la zone dans un fichier avec la transparence." #: f.widgets.cc:249 msgid "Trim/Crop margins and/or Rotate" msgstr "Rogner/Recadrer les marges et/ou Pivoter" #: f.widgets.cc:250 msgid "Upright a rotated image" msgstr "Redresser une image retournée" #: f.widgets.cc:251 f.widgets.cc:252 msgid "Fast auto enhance that may work OK" msgstr "" "Amélioration automatique rapide donnant généralement des résultats " "satisfaisants" #: f.widgets.cc:253 msgid "Adjust brightness, contrast, color" msgstr "Ajuster luminosité, contraste et couleur" #: f.widgets.cc:254 msgid "Add local contrast, enhance details" msgstr "Ajouter du contraste localement, améliorer des détails" #: f.widgets.cc:255 msgid "Adjust brightness distribution" msgstr "Ajuster la répartition de la luminosité" #: f.widgets.cc:256 msgid "Flatten zonal brightness distribution" msgstr "Aplatir la distribution zonale de luminosité" #: f.widgets.cc:257 msgid "Change pixel dimensions" msgstr "Changer la dimension des pixels" #: f.widgets.cc:258 msgid "Mirror image horizontally or vertically" msgstr "Miroir horizontal ou vertical de l'image" #: f.widgets.cc:259 msgid "Write text on image" msgstr "Écrire du texte sur l'image" #: f.widgets.cc:260 msgid "Write lines or arrows on image" msgstr "Tracer des lignes ou des flèches sur l'image" #: f.widgets.cc:261 msgid "Fix brightness uniformity across image" msgstr "Uniformiser la luminosité à travers l'image" #: f.widgets.cc:262 msgid "Paint edit function gradually with mouse" msgstr "Fonction d'édition de peinture graduelle au moyen de la souris" #: f.widgets.cc:263 msgid "Leverage edits by brightness or color" msgstr "Retouches à effet zonevier par luminosité ou couleur" #: f.widgets.cc:264 msgid "Edit plugins menu or run a plugin function" msgstr "Éditer le menu des greffons ou exéctuer une fonction de greffon" #: f.widgets.cc:267 msgid "Make the image look sharper" msgstr "Rendre l'image plus nette" #: f.widgets.cc:268 msgid "Make the image look fuzzy" msgstr "Donner un effet de flou à l'image" #: f.widgets.cc:269 msgid "Filter noise from low-light photos" msgstr "Filtrer le bruit de photos sous-exposées" #: f.widgets.cc:270 msgid "Remove unwanted objects" msgstr "Retirer les objets superflus" #: f.widgets.cc:271 msgid "Fix red-eyes from electronic flash" msgstr "Supprimer les yeux rouges dus au flash électronique" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Dessiner les pixels de l'image au moyen de la souris" #: f.widgets.cc:273 msgid "Paint image transparency using the mouse" msgstr "Peindre la transparence de l'image avec la souris" #: f.widgets.cc:274 msgid "Make BW/color, negative/positive, sepia" msgstr "Convertir NB/couleur, négatif/positif, sépia" #: f.widgets.cc:275 msgid "Shift/convert colors into other colors" msgstr "Changer/convertir des couleurs en d'autres couleurs" #: f.widgets.cc:276 msgid "Adjust color using RGB or CMY colors" msgstr "Ajuster les couleurs en utilisant les couleurs RGB ou CMY" #: f.widgets.cc:277 msgid "Adjust color using HSL colors" msgstr "Ajuster les couleurs en utilisant les couleurs HSL" #: f.widgets.cc:278 msgid "Adjust color in selected image areas" msgstr "Ajuster les couleurs dans les zones sélectionnées de l'image" #: f.widgets.cc:279 msgid "Match colors on one image with another" msgstr "Faire correspondre les couleurs d'une image avec celles d'une autre" #: f.widgets.cc:280 msgid "Convert to another color profile" msgstr "Convertir en un autre profil de couleurs" #: f.widgets.cc:281 msgid "Remove dust spots from scanned slides" msgstr "Ôter les grains de poussière des diapositives scannées" #: f.widgets.cc:282 msgid "Smoothen edges with jaggies" msgstr "Adoucir les bords crénelés" #: f.widgets.cc:283 msgid "Reduce Chromatic Abberation" msgstr "Réduire les Aberrations Chromatiques" #: f.widgets.cc:284 msgid "Erase known hot and dark pixels" msgstr "Effacer les pixels chauds et foncés connus" #: f.widgets.cc:287 msgid "Remove curvature, esp. panoramas" msgstr "Corriger la courbure, principalement des images panoramiques" #: f.widgets.cc:288 msgid "Straighten objects seen from an angle" msgstr "Redresser l'angle de vue des objets (perspective)" #: f.widgets.cc:289 msgid "Distort image areas using the mouse" msgstr "Déformer les zones de l'image au moyen de la souris" #: f.widgets.cc:290 f.widgets.cc:291 f.widgets.cc:292 msgid "Distort the whole image using the mouse" msgstr "Déformer l'image entière au moyen de la souris" #: f.widgets.cc:293 msgid "Flatten a photographed book page" msgstr "Aplatir une photographie de page de livre" #: f.widgets.cc:296 msgid "Reduce color depth (posterize)" msgstr "Réduire la profondeur de couleur (postérisation)" #: f.widgets.cc:297 msgid "Convert to pencil sketch" msgstr "Transformer en esquisse au crayon" #: f.widgets.cc:298 msgid "Convert to line drawing (edge detection)" msgstr "Convertir en dessin au trait (détection de bord)" #: f.widgets.cc:299 msgid "Convert to solid color drawing" msgstr "Convertir en dessin de couleur de base" #: f.widgets.cc:300 msgid "Graduated Blur depending on contrast" msgstr "Flou Progressif dépendant du contraste" #: f.widgets.cc:301 msgid "Create an embossed or 3D appearance" msgstr "Donner l'apparence d'un estampage ou tridimensionnelle" #: f.widgets.cc:302 msgid "Convert to square tiles" msgstr "Transformer en mosaïque" #: f.widgets.cc:303 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "Convertir en trame de points (effet Roy Lichtenstein)" #: f.widgets.cc:304 msgid "Convert into a simulated painting" msgstr "Simuler une peinture" #: f.widgets.cc:305 msgid "Change brightness or color radially" msgstr "Modifier la luminosité ou la couleur radialement" #: f.widgets.cc:306 msgid "Add texture to an image" msgstr "Ajouter une texture à une image" #: f.widgets.cc:307 msgid "Tile image with a repeating pattern" msgstr "Carreler l'image avec un motif répétitif" #: f.widgets.cc:308 msgid "Create a mosaic with tiles made from all images" msgstr "" "Créer une mosaïque par juxtaposition de motifs obtenus à partir de toutes " "les images " #: f.widgets.cc:309 msgid "Process an image using a custom kernel" msgstr "Traiter une image au moyen d'un noyau personnalisé" #: f.widgets.cc:310 msgid "Warp an image with a wave pattern" msgstr "Déformer une image au moyen d'un motif de vague" #: f.widgets.cc:311 msgid "Blur an image in the direction of mouse drags" msgstr "Flouter une image suivant la direction des glissements de souris" #: f.widgets.cc:312 msgid "Make a spherical projection of an image" msgstr "" #: f.widgets.cc:315 msgid "Combine bright/dark images for better detail" msgstr "Combiner les images claires/foncées pour affiner les détails" #: f.widgets.cc:316 msgid "Combine near/far focus images for deeper focus" msgstr "" "Combiner les mises au point proche/lointain d'images pour une netteté " "optimale" #: f.widgets.cc:317 msgid "Combine images to erase passing people, etc." msgstr "Combiner les images pour effacer les passants, etc." #: f.widgets.cc:318 msgid "Combine noisy images into a low-noise image" msgstr "" "Combiner des images fortement parasitées en une image légèrement parasitée" #: f.widgets.cc:319 msgid "Combine images into a panorama" msgstr "Combiner des images en une image panoramique" #: f.widgets.cc:320 msgid "Combine images into a vertical panorama" msgstr "Combiner des images en une image panoramique verticale" #: f.widgets.cc:321 msgid "Combine images into a panorama (panorama tools)" msgstr "Combiner les images en un panorama (outils panorama)" #: f.widgets.cc:322 msgid "Arrange images and text in a layout (montage)" msgstr "Arranger des images et du texte dans un montage" #: f.widgets.cc:325 msgid "Index new files and make thumbnails" msgstr "Indexer les nouveaux fichiers et créer les vignettes" #: f.widgets.cc:326 msgid "Change user preferences" msgstr "Modifier les préférences de l'utilisateur" #: f.widgets.cc:327 msgid "Change Keyboard Shortcut Keys" msgstr "Modifier les touches de Raccourcis Clavier" #: f.widgets.cc:328 msgid "Show a brightness distribution graph" msgstr "Montrer un graphique de répartition de la luminosité" #: f.widgets.cc:329 msgid "Show or revise grid lines" msgstr "Montrer ou régler les guides de la grille" #: f.widgets.cc:330 msgid "Change color of foreground lines" msgstr "Modifier la couleur de premier plan des lignes" #: f.widgets.cc:331 msgid "Show RGB colors at mouse click" msgstr "Montrer les couleurs RGB au clic de souris" #: f.widgets.cc:332 msgid "Magnify image around the mouse position" msgstr "Agrandir l'image autour de la postion du curseur" #: f.widgets.cc:333 msgid "Highlight darkest and brightest pixels" msgstr "Mettre les pixels les plus foncés/clairs en évidence" #: f.widgets.cc:334 msgid "Chart to adjust monitor color" msgstr "Diagramme d'ajustement de la couleur de l'écran" #: f.widgets.cc:335 msgid "Chart to adjust monitor gamma" msgstr "Diagramme d'ajustement du gamma de l'écran" #: f.widgets.cc:336 msgid "Change the GUI language" msgstr "Changer la langue de l'interface" #: f.widgets.cc:337 msgid "Report missing translations" msgstr "Faire un rapport des traductions manquantes" #: f.widgets.cc:338 msgid "Calibrate printer colors" msgstr "Calibrer les couleurs de l'imprimante" #: f.widgets.cc:339 msgid "Memory and CPU (to terminal/logfile)" msgstr "Mémoire et CPU (vers terminal/fichier-journal)" #: f.widgets.cc:343 msgid "Rename/convert/resize/move multiple files" msgstr "Renommer/convertir/redimensionner/déplacer fichier par lots" #: f.widgets.cc:344 msgid "Upright multiple rotated image files" msgstr "Redresser plusieurs fichiers images tournés" #: f.widgets.cc:345 msgid "Delete or Trash multiple files" msgstr "Supprimer ou Mettre plusieurs fichiers à la Corbeille" #: f.widgets.cc:346 msgid "Convert camera RAW files using DCraw" msgstr "Convertir les fichiers Photos RAW au moyen de DCraw" #: f.widgets.cc:347 msgid "Convert camera RAW files using Raw Therapee" msgstr "Convertir les fichiers Photos RAW au moyen de Raw Therapee" #: f.widgets.cc:348 msgid "Build and run edit script files" msgstr "" #: f.widgets.cc:349 msgid "Burn selected image files to CD or DVD" msgstr "Graver les fichiers images sélectionnés sur un CD/DVD" #: f.widgets.cc:350 msgid "Search all image files and report duplicates" msgstr "Rechercher tous les fichiers image et signaler les doublons" #: f.widgets.cc:354 msgid "Quick Start mini-guide" msgstr "Mini-guide pour un Démarrage Rapide" #: f.widgets.cc:355 msgid "Read the user guide" msgstr "Consulter le manuel utilisateur" #: f.widgets.cc:356 msgid "Recent user guide changes" msgstr "Manuel utilisateur des derniers changements" #: f.widgets.cc:357 msgid "Summary of image edit functions" msgstr "Sommaire des fonctions d’édition d'image" #: f.widgets.cc:358 msgid "Technical installation notes" msgstr "Notes techniques d'installation" #: f.widgets.cc:359 msgid "List updates by Fotoxx version" msgstr "Lister les mises à jour par version de Fotoxx" #: f.widgets.cc:360 msgid "View the log file and error messages" msgstr "Afficher le fichier-journal et les messages d'erreur" #: f.widgets.cc:361 msgid "How to do Fotoxx translations" msgstr "Comment traduire Fotoxx" #: f.widgets.cc:362 msgid "Show the Fotoxx web page" msgstr "Aller à la page web de Fotoxx" #: f.widgets.cc:363 msgid "Version, license, contact, credits" msgstr "Version, licence, contact, crédits" #: f.widgets.cc:366 msgid "Organize images into albums" msgstr "Organiser les images en albums" #: f.widgets.cc:367 msgid "Start a slide show" msgstr "Démarrer un diaporama" #: f.widgets.cc:368 msgid "Set desktop wallpaper from current Fotoxx image" msgstr "" #: f.widgets.cc:369 msgid "Cycle desktop wallpaper from a Fotoxx album" msgstr "" #: f.widgets.cc:391 msgid "New Window" msgstr "Nouvelle Fenêtre" #: f.widgets.cc:392 msgid "Sync Gallery" msgstr "" #: f.widgets.cc:393 msgid "Recently Seen Images" msgstr "Images Récemment Affichées" #: f.widgets.cc:394 msgid "Newest Images" msgstr "Images les plus Récentes" #: f.widgets.cc:396 msgid "Open Previous File" msgstr "Ouvrir l'Image Précédente" #: f.widgets.cc:397 msgid "Open RAW (ufraw)" msgstr "Ouvrir RAW (ufraw)" #: f.widgets.cc:398 msgid "Open RAW (Raw Therapee)" msgstr "Ouvrir RAW (Raw Therapee)" #: f.widgets.cc:399 msgid "New Blank Image" msgstr "Nouvelle Image Vierge" #: f.widgets.cc:402 msgid "Delete Image File" msgstr "Supprimer le Fichier Image" #: f.widgets.cc:403 msgid "Print Image" msgstr "Imprimer l'Image" #: f.widgets.cc:404 msgid "Print Calibrated Image" msgstr "" #: f.widgets.cc:405 f.widgets.cc:550 msgid "Set Desktop Wallpaper" msgstr "" #: f.widgets.cc:409 msgid "View Metadata (short)" msgstr "Afficher les Métadonnées (résumé)" #: f.widgets.cc:410 msgid "View Metadata (long)" msgstr "Afficher les Métadonnées (informations complètes)" #: f.widgets.cc:411 msgid "Show Captions on Image" msgstr "Afficher les Légendes de l'Image" #: f.widgets.cc:413 msgid "Edit Any Metadata" msgstr "Éditer n'importe Quelles Métadonnées" #: f.widgets.cc:416 f.widgets.cc:541 msgid "Batch Rename Tags" msgstr "Renommage de Balises par Lots" #: f.widgets.cc:417 f.widgets.cc:542 msgid "Batch Add/Change Metadata" msgstr "Ajout/Modificaton des Métadonnées par Lots" #: f.widgets.cc:420 f.widgets.cc:544 f.widgets.cc:618 msgid "Images by Geotag" msgstr "Images par Balises de Géolocalisation" #: f.widgets.cc:421 f.widgets.cc:545 f.widgets.cc:619 msgid "Search Images" msgstr "Rechercher des Images" #: f.widgets.cc:424 fotoxx.h:1168 msgid "Select" msgstr "Sélectionner" #: f.widgets.cc:425 fotoxx.h:1170 msgid "Show" msgstr "Montrer" #: f.widgets.cc:426 fotoxx.h:1124 msgid "Hide" msgstr "Masquer" #: f.widgets.cc:427 fotoxx.h:1110 msgid "Enable" msgstr "Activer" #: f.widgets.cc:428 fotoxx.h:1106 msgid "Disable" msgstr "Désactiver" #: f.widgets.cc:429 fotoxx.h:1127 msgid "Invert" msgstr "Inverser" #: f.widgets.cc:430 fotoxx.h:1184 msgid "Unselect" msgstr "Dé-sélectionner" #: f.widgets.cc:431 msgid "Copy Area" msgstr "Copier la Zone" #: f.widgets.cc:432 msgid "Paste Area" msgstr "Coller la Zone" #: f.widgets.cc:433 msgid "Open Area File" msgstr "Ouvrir un Fichier de Zone" #: f.widgets.cc:434 msgid "Save Area File" msgstr "Enregistrer le Fichier de Zone" #: f.widgets.cc:439 msgid "Voodoo 1" msgstr "Voodoo 1" #: f.widgets.cc:440 msgid "Voodoo 2" msgstr "Voodoo 2" #: f.widgets.cc:442 f.widgets.cc:687 msgid "Brightness Dist." msgstr "Répartition de la Luminosité" #: f.widgets.cc:443 f.widgets.cc:688 msgid "Zonal Flatten" msgstr "Aplatissement Zonal" #: f.widgets.cc:447 msgid "Add Text" msgstr "Ajouter du Texte" #: f.widgets.cc:448 msgid "Add Lines" msgstr "Ajouter des Lignes" #: f.widgets.cc:451 msgid "Plugins" msgstr "Greffons" #: f.widgets.cc:456 msgid "Denoise" msgstr "Réduction de Bruit" #: f.widgets.cc:458 msgid "Red Eyes" msgstr "Yeux Rouges" #: f.widgets.cc:463 msgid "Adjust RGB/CMY" msgstr "Ajuster RGB/CMY" #: f.widgets.cc:464 msgid "Adjust HSL" msgstr "Ajuster HSL" #: f.widgets.cc:465 msgid "Brightness Ramp" msgstr "Courbes de Luminosité" #: f.widgets.cc:467 msgid "Match Colors" msgstr "Faire Correspondre les couleurs" #: f.widgets.cc:468 msgid "Color Profile" msgstr "Profil de Couleur" #: f.widgets.cc:470 msgid "Anti-Alias" msgstr "Anticrénelage" #: f.widgets.cc:476 msgid "Fix Perspective" msgstr "Fixer la Perspective" #: f.widgets.cc:481 msgid "Flatten Book Page" msgstr "Aplatir une Page de Livre" #: f.widgets.cc:484 msgid "Color Depth" msgstr "Profondeur de Couleur" #: f.widgets.cc:485 msgid "Sketch" msgstr "Esquisse" #: f.widgets.cc:489 msgid "Embossing" msgstr "Estampage" #: f.widgets.cc:491 msgid "Dots" msgstr "Trame de points" #: f.widgets.cc:492 msgid "Painting" msgstr "Peinture" #: f.widgets.cc:494 msgid "Texture" msgstr "Texture" #: f.widgets.cc:496 msgid "Mosaic" msgstr "Mosaïque" #: f.widgets.cc:503 msgid "High Dynamic Range" msgstr "Fourchette Dynamique Étendue" #: f.widgets.cc:504 msgid "High Depth of Field" msgstr "Grande Profondeur de Champ" #: f.widgets.cc:505 msgid "Stack/Paint" msgstr "Superposer/Peindre" #: f.widgets.cc:506 msgid "Stack/Noise" msgstr "Superposition/Bruit" #: f.widgets.cc:507 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:508 msgid "Vertical Panorama" msgstr "Panorama Vertical" #: f.widgets.cc:509 msgid "PT Panorama" msgstr "PT Panorama" #: f.widgets.cc:510 msgid "Mashup" msgstr "Pêle-Mêle" #: f.widgets.cc:515 msgid "Keyboard Shortcuts" msgstr "Raccourcis Clavier" #: f.widgets.cc:516 msgid "Show Brightness Dist." msgstr "Afficher la distribution de luminostié" #: f.widgets.cc:521 msgid "Dark/Bright Pixels" msgstr "Pixels Foncés/Clairs" #: f.widgets.cc:522 msgid "Monitor Color" msgstr "Couleurs de l'Écran" #: f.widgets.cc:524 msgid "Change Language" msgstr "Changer de la Langue" #: f.widgets.cc:525 msgid "Missing Translations" msgstr "Traductions Manquantes" #: f.widgets.cc:527 msgid "Resources" msgstr "Ressources" #: f.widgets.cc:534 msgid "Batch RAW (DCraw)" msgstr "Traitement RAW par Lots (DCraw)" #: f.widgets.cc:535 msgid "Batch RAW (Raw Therapee)" msgstr "Traitement RAW par Lots (Raw Therapee)" #: f.widgets.cc:536 msgid "Script Files" msgstr "" #: f.widgets.cc:537 msgid "Burn Images to CD/DVD" msgstr "Graver les Images sur un CD/DVD" #: f.widgets.cc:551 msgid "Cycle Desktop Wallpaper" msgstr "" #: f.widgets.cc:568 f.widgets.cc:591 f.widgets.cc:613 msgid "Gallery" msgstr "Galerie" #: f.widgets.cc:569 f.widgets.cc:592 f.widgets.cc:614 msgid "World Maps" msgstr "Mappemondes" #: f.widgets.cc:571 msgid "Favorites" msgstr "Favoris" #: f.widgets.cc:573 fotoxx.h:1164 msgid "Save" msgstr "Enregistrer" #: f.widgets.cc:574 msgid "Prev/Next" msgstr "Préc/Suiv" #: f.widgets.cc:575 msgid "Metadata" msgstr "Métadonnées" #: f.widgets.cc:576 msgid "Areas" msgstr "Zones" #: f.widgets.cc:577 fotoxx.h:1109 msgid "Edit" msgstr "Éditer" #: f.widgets.cc:578 msgid "Repair" msgstr "Réparer" #: f.widgets.cc:579 msgid "Bend" msgstr "Cintrer" #: f.widgets.cc:580 msgid "Effects" msgstr "Effets" #: f.widgets.cc:581 msgid "Combine" msgstr "Combiner" #: f.widgets.cc:582 msgid "Undo/Redo" msgstr "Annuler/Rétablir" #: f.widgets.cc:583 msgid "Tools" msgstr "Outils" #: f.widgets.cc:594 msgid "Sync.G" msgstr "Sync. G" #: f.widgets.cc:595 msgid "Albums" msgstr "Albums" #: f.widgets.cc:596 msgid "Bookmarks" msgstr "Marque-pages" #: f.widgets.cc:605 msgid "Batch" msgstr "Traitement par Lots" #: f.widgets.cc:616 msgid "Choose Map" msgstr "Sélectionner la Carte" #: f.widgets.cc:617 msgid "Search Range" msgstr "Fourchette de Recherche" #: f.widgets.cc:625 fotoxx.h:1182 msgid "Undo" msgstr "Rétablir" #: f.widgets.cc:626 fotoxx.h:1155 msgid "Redo" msgstr "Rétablir" #: f.widgets.cc:667 msgid "Popup Image" msgstr "Image Contextuelle" #: f.widgets.cc:668 msgid "Popup Image (add)" msgstr "Image contextuelle (ajout)" #: f.widgets.cc:672 fotoxx.h:1158 msgid "Rename" msgstr "Renommer" #: f.widgets.cc:673 msgid "Copy to Location" msgstr "Copier vers l'Emplacement" #: f.widgets.cc:674 msgid "Move to Location" msgstr "Déplacer vers l'Emplacement" #: f.widgets.cc:675 msgid "Copy to Clipboard" msgstr "Copier dans le Presse-Papier" #: f.widgets.cc:676 msgid "Remove from Album" msgstr "Retirer de l'Album" #: f.widgets.cc:677 msgid "Cut to Image Cache" msgstr "Couper vers le Cache de l'Image" #: f.widgets.cc:678 msgid "Copy to Image Cache" msgstr "Copier vers le Cache de l'Image" #: f.widgets.cc:679 msgid "Paste Image Cache Here (clear)" msgstr "Coller le Cache de l'Image Ici (effacer)" #: f.widgets.cc:680 msgid "Paste Image Cache Here (keep)" msgstr "Coller le Cache de l'Image Ici (conserver)" #: f.widgets.cc:681 msgid "Paste Current Image File Here" msgstr "Coller le Fichier Image actuel Ici" #: f.widgets.cc:685 msgid "Voodoo Enhance" msgstr "Amélioration Voodoo" #: f.widgets.cc:690 msgid "Select Area" msgstr "Sélectionner une Zone" #: f.widgets.cc:693 fotoxx.h:1178 msgid "Trash" msgstr "Corbeille" #: f.widgets.cc:819 msgid "Image Locations" msgstr "Emplacements d'Images" #: fotoxx-15.11.cc:373 msgid "Please install missing programs:" msgstr "Veuillez installer ce(s) programme(s) manquant(s):" #: fotoxx-15.11.cc:662 msgid "Kill active dialog?" msgstr "Terminer le dialogue actif?" #: fotoxx-15.11.cc:733 msgid "(reduced)" msgstr "(réduit)" #: fotoxx-15.11.cc:734 msgid "area active" msgstr "zone active" #: fotoxx-15.11.cc:735 msgid "dialog open" msgstr "dialogue ouvert" #: fotoxx-15.11.cc:736 msgid "blocked" msgstr "bloqué" #: fotoxx-15.11.cc:786 msgid "edits" msgstr "opérations" #: fotoxx-15.11.cc:2793 msgid "Exceed 50 anchor points" msgstr "Excède 50 points d'ancrage" #: fotoxx-15.11.cc:3013 msgid "load curve from a file" msgstr "charger la courbe depuis un fichier" #: fotoxx-15.11.cc:3079 msgid "curve file is invalid" msgstr "" #: fotoxx-15.11.cc:3093 msgid "save curve to a file" msgstr "enregistrer les courbes dans un fichier" #: fotoxx-15.11.cc:3165 msgid "Too many edits, please save image" msgstr "Trop d'opérations, veuillez enregistrer l'image" #: fotoxx-15.11.cc:3170 msgid "this function cannot be scripted" msgstr "" #: fotoxx-15.11.cc:3187 msgid "" "Select area not active.\n" "Continue?" msgstr "" "La sélection de zone n'est pas active.\n" "Continuer?" #: fotoxx-15.11.cc:3499 msgid "file data does not fit dialog" msgstr "les données de fichier ne sont pas adaptées à la boite de dialogue" #: fotoxx-15.11.cc:3509 msgid "Save settings to a file" msgstr "Sauvegarder les paramètres dans un fichier" #: fotoxx-15.11.cc:3825 msgid "This action will discard changes to current image" msgstr "Cette action va annuler les modifications apportées à l'image actuelle" #: fotoxx-15.11.cc:3826 msgid "prior function still active" msgstr "fonction antérieure toujours active" #: fotoxx-15.11.cc:3827 fotoxx.h:1128 msgid "Keep" msgstr "Conserver" #: fotoxx-15.11.cc:3828 msgid "Discard" msgstr "Abandonner" #: fotoxx.h:1080 msgid "Add" msgstr "Ajouter" #: fotoxx.h:1081 msgid "Add All" msgstr "Tout ajouter" #: fotoxx.h:1083 msgid "Amount" msgstr "Quantité" #: fotoxx.h:1084 msgid "Angle" msgstr "Angle" #: fotoxx.h:1085 msgid "Apply" msgstr "Appliquer" #: fotoxx.h:1086 msgid "Auto" msgstr "Auto" #: fotoxx.h:1087 msgid "Black" msgstr "Noir" #: fotoxx.h:1088 msgid "Blend Width" msgstr "Dosage du Mélange" #: fotoxx.h:1090 zfuncs.cc:10098 msgid "bottom" msgstr "bas" #: fotoxx.h:1092 msgid "Browse" msgstr "Parcourir" #: fotoxx.h:1093 msgid "Cancel" msgstr "Annuler" #: fotoxx.h:1094 msgid "Center" msgstr "centrer" #: fotoxx.h:1095 msgid "Choose" msgstr "Choisir" #: fotoxx.h:1096 msgid "Clear" msgstr "Effacer" #: fotoxx.h:1097 msgid "Color" msgstr "couleur" #: fotoxx.h:1098 msgid "continue" msgstr "Continuer" #: fotoxx.h:1100 msgid "Copy" msgstr "Copier" #: fotoxx.h:1101 msgid "Create" msgstr "Créer" #: fotoxx.h:1102 msgid "Curve File:" msgstr "Fichier de Courbes:" #: fotoxx.h:1103 msgid "Cut" msgstr "Couper" #: fotoxx.h:1104 msgid "Deband" msgstr "Détendre" #: fotoxx.h:1105 msgid "Delete" msgstr "Supprimer" #: fotoxx.h:1107 msgid "Done" msgstr "Terminé" #: fotoxx.h:1108 msgid "edge" msgstr "bord" #: fotoxx.h:1111 msgid "Erase" msgstr "Effacer" #: fotoxx.h:1112 msgid "Fetch" msgstr "Atteindre" #: fotoxx.h:1113 msgid "output file already exists" msgstr "fichier de sortie déjà existant" #: fotoxx.h:1114 #, c-format msgid "%d files selected" msgstr "%d fichiers sélectionnés" #: fotoxx.h:1115 msgid "Find" msgstr "Rechercher" #: fotoxx.h:1116 msgid "Finish" msgstr "Finir" #: fotoxx.h:1117 msgid "Flatten" msgstr "Aplatir" #: fotoxx.h:1118 msgid "Font" msgstr "Police" #: fotoxx.h:1119 msgid "Geotags" msgstr "Balises de Géolocalisation" #: fotoxx.h:1121 msgid "Grid" msgstr "Grille" #: fotoxx.h:1122 msgid "Height" msgstr "Hauteur" #: fotoxx.h:1125 msgid "Images" msgstr "Images" #: fotoxx.h:1126 msgid "Insert" msgstr "Insérer" #: fotoxx.h:1129 zfuncs.cc:10102 msgid "left" msgstr "gauche" #: fotoxx.h:1130 msgid "limit" msgstr "limiter" #: fotoxx.h:1131 msgid "Load" msgstr "Charger" #: fotoxx.h:1132 msgid "Make" msgstr "Faire" #: fotoxx.h:1134 msgid "Map" msgstr "Mappemonde" #: fotoxx.h:1135 msgid "Max" msgstr "Max" #: fotoxx.h:1136 msgid "Negative" msgstr "Négatif" #: fotoxx.h:1137 msgid "New" msgstr "Nouveau" #: fotoxx.h:1138 msgid "Next" msgstr "Suiv." #: fotoxx.h:1139 zfuncs.cc:9190 msgid "No" msgstr "Non" #: fotoxx.h:1140 msgid "no images" msgstr "pas d'images" #: fotoxx.h:1142 msgid "no selection" msgstr "pas de sélection" #: fotoxx.h:1143 msgid "OK" msgstr "Valider" #: fotoxx.h:1145 msgid "Paste" msgstr "Coller" #: fotoxx.h:1146 msgid "Pause" msgstr "Pause" #: fotoxx.h:1147 msgid "Percent" msgstr "Pour-cent" #: fotoxx.h:1149 msgid "Presets" msgstr "Pré-réglages" #: fotoxx.h:1150 msgid "Prev" msgstr "Préc." #: fotoxx.h:1151 msgid "Proceed" msgstr "Poursuivre" #: fotoxx.h:1153 msgid "range" msgstr "fourchette" #: fotoxx.h:1156 msgid "Reduce" msgstr "Réduire" #: fotoxx.h:1157 msgid "Remove" msgstr "Retirer" #: fotoxx.h:1159 msgid "Replace" msgstr "Remplacer" #: fotoxx.h:1160 msgid "Reserved" msgstr "Réservé" #: fotoxx.h:1161 msgid "Reset" msgstr "Réinitialiser" #: fotoxx.h:1162 zfuncs.cc:10106 msgid "right" msgstr "droit" #: fotoxx.h:1163 msgid "Rotate" msgstr "Pivoter" #: fotoxx.h:1165 msgid "Unknown file type, save as tiff/jpeg/png to edit" msgstr "" "Type de fichier inconnu. Enregistrer au format tiff/jpeg/png pour l'éditer" #: fotoxx.h:1166 msgid "Search" msgstr "Rechercher" #: fotoxx.h:1167 msgid "Seconds" msgstr "Secondes" #: fotoxx.h:1171 msgid "Size" msgstr "Taille" #: fotoxx.h:1172 msgid "Start" msgstr "Démarrer" #: fotoxx.h:1173 msgid "Strength" msgstr "Force" #: fotoxx.h:1174 msgid "Threshold" msgstr "Seuil" #: fotoxx.h:1175 #, c-format msgid "exceed %d files" msgstr "excède %d fichiers" #: fotoxx.h:1176 zfuncs.cc:10094 msgid "top" msgstr "haut" #: fotoxx.h:1177 msgid "Transparency" msgstr "Transparence" #: fotoxx.h:1179 msgid "Trim" msgstr "Rogner" #: fotoxx.h:1180 msgid "Undo All" msgstr "Tout Annuler" #: fotoxx.h:1181 msgid "Undo Last" msgstr "Annuler Dernière" #: fotoxx.h:1183 msgid "Unfinish" msgstr "Ne pas finir" #: fotoxx.h:1185 msgid "View" msgstr "Afficher" #: fotoxx.h:1186 msgid "Web" msgstr "Web" #: fotoxx.h:1187 msgid "White" msgstr "Blanc" #: fotoxx.h:1188 msgid "Width" msgstr "Largeur" #: fotoxx.h:1189 msgid "x-offset" msgstr "décalage x" #: fotoxx.h:1190 msgid "y-offset" msgstr "décalage y" #: fotoxx.h:1191 zfuncs.cc:9190 msgid "Yes" msgstr "Oui" #: zfuncs.cc:1601 #, c-format msgid "" "create directory? \n" " %s" msgstr "" "créer le répertoire? \n" " %s" #: zfuncs.cc:4378 msgid "user guide not found" msgstr "manuel utilisateur introuvable" #: zfuncs.cc:5196 #, c-format msgid "cannot open file %s" msgstr "impossible d'ouvrir le fichier %s" #: zfuncs.cc:5232 msgid "save screen to file" msgstr "enregistrer l'écran dans un fichier" #: zfuncs.cc:9266 zfuncs.cc:9761 zfuncs.cc:10081 msgid "cancel" msgstr "annuler" #: zfuncs.cc:9722 msgid "choose file" msgstr "choisir le fichier" #: zfuncs.cc:9727 msgid "choose files" msgstr "choisir les fichiers" #: zfuncs.cc:9732 msgid "save" msgstr "enregistrer" #: zfuncs.cc:9738 msgid "choose folder" msgstr "choisir le dossier" #: zfuncs.cc:9743 msgid "choose folders" msgstr "choisir les dossiers" #: zfuncs.cc:9748 msgid "create folder" msgstr "créer un dossier" #: zfuncs.cc:9755 msgid "hidden" msgstr "masqué" #: zfuncs.cc:10081 msgid "done" msgstr "terminé" #: zfuncs.cc:10081 zfuncs.cc:10090 msgid "margins" msgstr "marges" #: zfuncs.cc:10111 msgid "image scale" msgstr "échelle de l'image" #: zfuncs.cc:10113 msgid "percent" msgstr "pourcent" #: zfuncs.cc:10120 msgid "image" msgstr "image" #: zfuncs.cc:10124 msgid "width" msgstr "largeur" #: zfuncs.cc:10128 msgid "height" msgstr "hauteur" #~ msgid "" #~ "Set the file name and location \n" #~ "for the output color map file." #~ msgstr "" #~ "Définir le nom et l'emplacement du fichier \n" #~ "du fichier de sortie de la carte des couleurs." #~ msgid "Median Brightness" #~ msgstr "Luminosité Médiane" #~ msgid "Flatten Outliers 2" #~ msgstr "Écraser les valeurs atypiques 2" #~ msgid "Flatten Outliers 1" #~ msgstr "Écraser les valeurs atypiques 1" #~ msgid "Kuwahara method" #~ msgstr "méthode Kuwahara" #~ msgid "brightness gradient" #~ msgstr "dégradé de luminosité" #~ msgid "unsharp mask" #~ msgstr "masque flou" #~ msgid "Lock aspect ratio" #~ msgstr "Verrouiller les proportions" #~ msgid "Surfaces" #~ msgstr "Surfaces" #~ msgid "Adjust Brightness Dist." #~ msgstr "Ajuster la Distribution de Luminosité" #~ msgid "" #~ "new name/base/adder unreasonable\n" #~ " e.g. newname ### 100 10" #~ msgstr "" #~ "nouveau nom/base/ajouts incohérents\n" #~ " ex: nouveaunom### 100 10" #~ msgid "Cycle Desktop Image" #~ msgstr "Cycle Image Bureau" #~ msgid "Set Desktop Image" #~ msgstr "Définir l'Image du Bureau" #~ msgid "Cycle desktop images from a Fotoxx album" #~ msgstr "Cycle d'images du bureau à partir d'un album Fotoxx" #~ msgid "Set desktop image from current Fotoxx image" #~ msgstr "Définir l'image du bureau à partir de l'image Fotoxx actuelle" #~ msgid "Albums: Manage Albums, Slide Show, Desktop Background" #~ msgstr "Albums: Gestion des Albums, Diaporama, Fond d'Écran" #~ msgid "jpeg quality must be 1-100" #~ msgstr "la qualité JPEG doit être comprise entre 1 et 100" #~ msgid "(%d images)" #~ msgstr "(%d images)" #~ msgid "(0 images)" #~ msgstr "(0 images)" fotoxx-15.11.1/locales/translate-es.po0000664000175000017500000032735512616075370016263 0ustar micomico# Traducción al castellano para el paquete Fotoxx. # Copyright (C) 2011 THE home'S COPYRIGHT HOLDER # Este archivo está distribuido bajo la misma licencia que el paquete principal. # Miguel Anxo Bouzada , 2010, 2011,2012, # Josep Antoni Miralles Puignau , 2012,2013,2014, 2015 # msgid "" msgstr "" "Project-Id-Version: home 2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-10-28 22:08+0100\n" "PO-Revision-Date: 2013-12-29 23:39+0100\n" "Last-Translator: Josep Antoni Miralles Puignau \n" "Language-Team: nLanguage: es\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:78 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:80 msgid "Drag album thumbnail to new position." msgstr "Arrastrar miniatura del álbum a la nueva posición" #: f.albums.cc:109 f.widgets.cc:548 msgid "Manage Albums" msgstr "Gestionar álbum" #: f.albums.cc:120 f.albums.cc:258 msgid "Create or replace an album" msgstr "Crear o reemplazar un álbum" #: f.albums.cc:122 msgid "Album to view or edit" msgstr "Álbum para ver o editar" #: f.albums.cc:124 msgid "Select images, add to cache" msgstr "Seleccionar imágenes, añadir al caché" #: f.albums.cc:126 msgid "Clear image cache" msgstr "Limpiar caché de imagen" #: f.albums.cc:128 f.albums.cc:150 msgid "Delete an album" msgstr "Borrar un álbum" #: f.albums.cc:149 msgid "Choose Album" msgstr "Elegir álbum" #: f.albums.cc:151 #, c-format msgid "Image cache has %d images" msgstr "" #: f.albums.cc:152 msgid "cache added to empty album" msgstr "caché añadida a álbum vacío" #: f.albums.cc:207 #, c-format msgid "delete %s ?" msgstr "¿borrar %s ?" #: f.albums.cc:242 #, c-format msgid "fill from image cache (%d images)" msgstr "llenar desde caché (%d imágenes)" #: f.albums.cc:260 f.albums.cc:297 msgid "Album Name" msgstr "Nombre del álbum" #: f.albums.cc:266 msgid "make an initially empty album" msgstr "crear un álbum vacío" #: f.albums.cc:268 msgid "fill from current gallery" msgstr "llenar desde la galería actual" #: f.albums.cc:311 msgid "enter an album name" msgstr "introducir un nombre de álbum" #: f.albums.cc:338 msgid "gallery is empty" msgstr "la galería está vacía" #: f.albums.cc:365 msgid "new album created" msgstr "nuevo álbum creado" #: f.albums.cc:1099 msgid "Press ESC to exit slide show" msgstr "Pulse ESC para salir del diaporama" #: f.albums.cc:1101 msgid "instant" msgstr "instantánea" #: f.albums.cc:1102 msgid "fade-in" msgstr "fundido" #: f.albums.cc:1103 msgid "roll-right" msgstr "desenrollar hacia la derecha" #: f.albums.cc:1104 msgid "roll-down" msgstr "desenrollar hacia abajo" #: f.albums.cc:1105 msgid "venetian" msgstr "persiana" #: f.albums.cc:1106 msgid "grate" msgstr "enrejado" #: f.albums.cc:1107 msgid "rectangle" msgstr "rectángulo" #: f.albums.cc:1108 msgid "implode" msgstr "implosión" #: f.albums.cc:1109 msgid "explode" msgstr "explosión" #: f.albums.cc:1110 msgid "radar" msgstr "radar" #: f.albums.cc:1111 msgid "Japanese-fan" msgstr "abanico japonés" #: f.albums.cc:1112 msgid "jaws" msgstr "dientes" #: f.albums.cc:1113 msgid "ellipse" msgstr "elipse" #: f.albums.cc:1114 msgid "raindrops" msgstr "gotas de lluvia" #: f.albums.cc:1115 msgid "doubledoor" msgstr "doble puerta" #: f.albums.cc:1116 msgid "rotate" msgstr "girar" #: f.albums.cc:1117 msgid "fallover" msgstr "hacia abajo" #: f.albums.cc:1118 msgid "sphereoid" msgstr "" #: f.albums.cc:1139 f.widgets.cc:549 msgid "Slide Show" msgstr "Diaporama" #: f.albums.cc:1150 msgid "Clip Limit" msgstr "Límite de secuencia" #: f.albums.cc:1154 msgid "Music File" msgstr "Archivo de Música" #: f.albums.cc:1159 msgid "Full Screen" msgstr "Pantalla completa" #: f.albums.cc:1160 msgid "Auto-replay" msgstr "Auto reiniciar" #: f.albums.cc:1166 msgid "Customize:" msgstr "Personalizar:" #: f.albums.cc:1167 msgid "transitions" msgstr "transiciones" #: f.albums.cc:1168 msgid "image files" msgstr "archivos de imagen" #: f.albums.cc:1173 f.albums.cc:1289 #, c-format msgid "%d images" msgstr "%d imágenes" #: f.albums.cc:1195 f.albums.cc:1259 f.albums.cc:1349 f.albums.cc:1462 #: f.albums.cc:1655 f.albums.cc:1739 f.albums.cc:1756 f.albums.cc:1977 msgid "invalid album" msgstr "álbum inválido" #: f.albums.cc:1273 msgid "open album" msgstr "abrir álbum" #: f.albums.cc:1305 msgid "Select music file" msgstr "Seleccione archivo de música" #: f.albums.cc:1329 msgid "select random (if 5+ enabled)" msgstr "selección aleatoria (si activado +5)" #: f.albums.cc:1353 msgid "Transition Preferences" msgstr "Preferencias de transición" #: f.albums.cc:1362 msgid "transition" msgstr "transición" #: f.albums.cc:1363 msgid "enabled" msgstr "activado" #: f.albums.cc:1364 msgid "slowdown" msgstr "ralentizar" #: f.albums.cc:1365 msgid "preference" msgstr "preferencia" #: f.albums.cc:1468 msgid "Image Preferences" msgstr "Preferencias de imagen" #: f.albums.cc:1472 f.file.cc:1159 f.file.cc:1400 f.file.cc:1608 f.meta.cc:437 msgid "Image File:" msgstr "Imagen:" #: f.albums.cc:1476 msgid "Play tone when image shows" msgstr "Reproducir música mientras se ve la imagen" #: f.albums.cc:1479 msgid "Show image caption" msgstr "Mostrar título del diaporama" #: f.albums.cc:1484 msgid "Show image comments" msgstr "Mostrar comentarios de la imagen" #: f.albums.cc:1489 msgid "Wait before zoom" msgstr "Espera antes del zoom" #: f.albums.cc:1494 msgid "Zoom type:" msgstr "Tipo de zoom:" #: f.albums.cc:1495 msgid "none" msgstr "ninguno" #: f.albums.cc:1496 msgid "zoom-in" msgstr "acercar" #: f.albums.cc:1497 msgid "zoom-out" msgstr "alejar" #: f.albums.cc:1500 msgid "Zoom size (x)" msgstr "Tamaño de zoom (x)" #: f.albums.cc:1503 msgid "Steps" msgstr "Pasos" #: f.albums.cc:1507 msgid "Zoom image location:" msgstr "Localización del zoom de imagen" #: f.albums.cc:1515 msgid "Wait after zoom" msgstr "Espera después del zoom" #: f.albums.cc:1520 msgid "Transition to next image" msgstr "Transición a la próxima imagen" #: f.albums.cc:1523 f.albums.cc:1625 msgid "next" msgstr "siguiente" #: f.albums.cc:1962 f.albums.cc:2120 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "error de formato de archivo: \n" " %s" #: f.area.cc:79 msgid "Select Area for Edits" msgstr "Seleccionar área a editar" #: f.area.cc:80 f.edit.cc:5927 msgid "Press F1 for help" msgstr "Pulsar F1 para ayuda" #: f.area.cc:89 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:119 msgid "select rectangle" msgstr "seleccionar rectángulo" #: f.area.cc:120 msgid "select ellipse" msgstr "seleccionar elipse" #: f.area.cc:123 msgid "freehand draw" msgstr "dibujar a mano alzada" #: f.area.cc:124 msgid "follow edge" msgstr "seguir contorno" #: f.area.cc:125 msgid "draw/replace" msgstr "dibujar/reemplazar" #: f.area.cc:128 msgid "select area within mouse" msgstr "seleccionar área con el ratón" #: f.area.cc:131 msgid "select one matching color within mouse" msgstr "seleccionar un color coincidente con el ratón" #: f.area.cc:135 msgid "select all matching colors within mouse" msgstr "seleccionar todos los colores coincidentes con el ratón" #: f.area.cc:139 f.edit.cc:5972 msgid "mouse radius" msgstr "radio del cursor" #: f.area.cc:142 msgid "match level %" msgstr "nivel de coincidencia %" #: f.area.cc:147 msgid "search range" msgstr "rango de búsqueda" #: f.area.cc:149 msgid "firewall" msgstr "corta fuegos" #: f.area.cc:152 msgid "Area Edge Blend Width" msgstr "Ancho de mezcla de bordes de área" #: f.area.cc:155 msgid "Edge Creep" msgstr "Borde progresivo" #: f.area.cc:160 msgid "Line Color:" msgstr "Color de línea:" #: f.area.cc:180 f.area.cc:181 msgid "area edits fade away within edge distance" msgstr "las ediciones de área se desvanecen en los bordes" #: f.area.cc:360 f.area.cc:543 #, c-format msgid "exceed %d edits" msgstr "excedido en %d ediciones" #: f.area.cc:1364 msgid "" "Click one time inside each enclosed area \n" "(possible gaps in the outline will be found). \n" "Press F1 for help." msgstr "" "Pulsar una vez dentro de cada área delimitada \n" "(pueden ser hallados posibles huecos en el contorno). \n" "Pulsar F1 para obtener ayuda." #: f.area.cc:1392 msgid "finish area" msgstr "terminar área" #: f.area.cc:1400 msgid "extend to corner:" msgstr "extender hasta la esquina" #: f.area.cc:1441 msgid "searching" msgstr "buscando" #: f.area.cc:1472 msgid "outline has a gap" msgstr "el contorno tiene un hueco" #: f.area.cc:1476 msgid "success" msgstr "satisfactorio" #: f.area.cc:1613 f.area.cc:1640 #, c-format msgid "found %d pixels" msgstr "encontrados %d pixels" #: f.area.cc:1935 f.area.cc:1952 f.area.cc:1972 f.area.cc:2287 f.area.cc:2363 #: f.area.cc:2520 msgid "the area is not finished" msgstr "el área no está cerrada" #: f.area.cc:2061 msgid "Edge calculation in progress" msgstr "Cálculo del contorno en progreso" #: f.area.cc:2070 msgid "Area Edge Calc" msgstr "Cálculo del contorno del área" #: f.area.cc:2471 msgid "load area from a file" msgstr "Cargar área desde un archivo" #: f.area.cc:2554 msgid "save area as a PNG file" msgstr "guardar área como un archivo PNG" #: f.area.cc:2582 msgid "position with mouse click/drag" msgstr "posición con el clic/arrastre del ratón" #: f.area.cc:2638 msgid "Paste Image" msgstr "Pegar imagen" #: f.area.cc:2643 f.batch.cc:1169 f.batch.cc:1660 msgid "resize" msgstr "redimensionar" #: f.batch.cc:107 f.widgets.cc:531 msgid "Batch Convert" msgstr "Convertir en lote" #: f.batch.cc:114 f.file.cc:942 msgid "New Name" msgstr "nombre nuevo" #: f.batch.cc:118 msgid "Sequence Numbers" msgstr "" #: f.batch.cc:120 msgid "base" msgstr "inicio" #: f.batch.cc:122 msgid "adder" msgstr "incremento" #: f.batch.cc:127 msgid "New Location" msgstr "Nueva ubicación" #: f.batch.cc:132 msgid "New File Type" msgstr "Nuevo tipo de archivo" #: f.batch.cc:136 f.batch.cc:144 f.repair.cc:3861 msgid "no change" msgstr "sin cambios" #: f.batch.cc:139 msgid "max. Width" msgstr "Ancho máx." #: f.batch.cc:147 f.batch.cc:565 msgid "Delete Originals" msgstr "Borrar originales" #: f.batch.cc:148 f.batch.cc:562 msgid "Copy Metadata" msgstr "Copiar metadatos" #: f.batch.cc:149 f.batch.cc:563 f.edit.cc:1413 f.widgets.cc:438 #: f.widgets.cc:684 msgid "Upright" msgstr "Enderezar" #: f.batch.cc:152 f.batch.cc:564 f.batch.cc:1178 f.batch.cc:1669 #: f.repair.cc:118 f.widgets.cc:454 msgid "Sharpen" msgstr "Enfocar" #: f.batch.cc:154 f.batch.cc:1180 f.batch.cc:1671 msgid "amount" msgstr "cantidad" #: f.batch.cc:157 f.batch.cc:1183 f.batch.cc:1674 msgid "threshold" msgstr "umbral" #: f.batch.cc:280 msgid "file type not supported" msgstr "tipo de archivo no soportad" #: f.batch.cc:344 msgid "cannot create new file" msgstr "no se puede crear el nuevo archivo" #: f.batch.cc:391 msgid "updating albums ..." msgstr "actualizando álbums ..." #: f.batch.cc:464 f.batch.cc:1456 f.batch.cc:1862 f.file.cc:1208 msgid "Select directory" msgstr "seleccionar directorio" #: f.batch.cc:513 msgid "" "new name/base/adder unreasonable\n" " e.g. newname ### 100 10\n" " or ... [oldname] ..." msgstr "" #: f.batch.cc:537 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "el tamaño máximo %d x %d no es razonable" #: f.batch.cc:556 #, c-format msgid "Convert %d image files" msgstr "Convertit %d imágenes" #: f.batch.cc:557 msgid "Rename to" msgstr "Renombrar como" #: f.batch.cc:558 msgid "Convert to" msgstr "Convertir a" #: f.batch.cc:559 msgid "Resize within" msgstr "Redimensionar dentro de " #: f.batch.cc:560 msgid "Output to" msgstr "Salida como" #: f.batch.cc:566 msgid "PROCEED?" msgstr "PROCEDER?" #: f.batch.cc:720 f.widgets.cc:532 msgid "Batch Upright" msgstr "Enderezar en lote" #: f.batch.cc:726 msgid "Survey all files" msgstr "Examinar todos los archivos" #: f.batch.cc:780 msgid "file cannot be read" msgstr "no se puede leer el archivo" #: f.batch.cc:898 msgid "cannot select both options" msgstr "no se pueden seleccionar ambas opciones" #: f.batch.cc:942 f.widgets.cc:533 msgid "Batch Delete/Trash" msgstr "Borrar/papelera en lote" #: f.batch.cc:947 msgid "delete" msgstr "borrar" #: f.batch.cc:950 msgid "trash" msgstr "papelera" #: f.batch.cc:1112 msgid "Batch Convert RAW Files (DCraw)" msgstr "Convertir en lote archivos RAW (DCraw)" #: f.batch.cc:1121 msgid "DCraw not installed" msgstr "DCraw no instalado" #: f.batch.cc:1156 f.batch.cc:1647 msgid "output location" msgstr "ubicación exterior" #: f.batch.cc:1161 f.batch.cc:1652 msgid "output file type" msgstr "tipo de archivo de salida" #: f.batch.cc:1192 msgid "white balance" msgstr "balance de blancos" #: f.batch.cc:1193 msgid "interpolation" msgstr "interpolacion" #: f.batch.cc:1194 msgid "color space" msgstr "espacio de color" #: f.batch.cc:1195 msgid "gamma curve" msgstr "curva Gamma" #: f.batch.cc:1198 msgid "camera" msgstr "cámara" #: f.batch.cc:1199 msgid "fixed" msgstr "fijado" #: f.batch.cc:1200 msgid "calculated" msgstr "calculado" #: f.batch.cc:1217 msgid "default" msgstr "por defecto" #: f.batch.cc:1223 msgid "defaults" msgstr "por defecto" #: f.batch.cc:1610 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Convertir en lote archivos RAW (RawTherapee)" #: f.batch.cc:1619 f.file.cc:428 msgid "Raw Therapee not installed" msgstr "RawTherapee no instalado" #: f.batch.cc:1959 msgid "start" msgstr "iniciar" #: f.batch.cc:1960 msgid "begin making a script file" msgstr "iniciando construcción de un script" #: f.batch.cc:1962 msgid "close" msgstr "cerrar" #: f.batch.cc:1963 msgid "finish making a script file" msgstr "acabando construcción de un script" #: f.batch.cc:1965 msgid "run" msgstr "ejecutar" #: f.batch.cc:1966 msgid "execute a script file" msgstr "ejecutar un script" #: f.batch.cc:2020 msgid "script already started" msgstr "script iniciado" #: f.batch.cc:2024 msgid "open new script file" msgstr "abrir nuevo script" #: f.batch.cc:2056 msgid "script file error" msgstr "error de script" #: f.batch.cc:2061 #, c-format msgid "%s added to script" msgstr "%s añadido al script" #: f.batch.cc:2074 msgid "no script file was started" msgstr "no se ha iniciado el script" #: f.batch.cc:2082 msgid "script file closed" msgstr "script cerrado" #: f.batch.cc:2101 msgid "script file is not closed" msgstr "el script no se ha cerrado" #: f.batch.cc:2105 msgid "select script file to run" msgstr "seleccionar script a ejecutar" #: f.batch.cc:2107 msgid "open script file" msgstr "abrir script" #: f.batch.cc:2114 msgid "unknown script file" msgstr "script desconocido" #: f.batch.cc:2121 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" "script: %s \n" " %s" #: f.batch.cc:2132 msgid "select image files to be processed" msgstr "seleccionaar imágenes a procesar" #: f.batch.cc:2144 f.batch.cc:2193 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "fallo de apertura: %s \n" " %s" #: f.batch.cc:2165 #, c-format msgid "unknown edit function: %s" msgstr "función de edición desconocida: %s" #: f.batch.cc:2174 #, c-format msgid "load widgets failed: %s" msgstr "cargar complementos fallados: %s" #: f.batch.cc:2203 #, c-format msgid "script file format error: %s" msgstr "error de formato de script: %s" #: f.batch.cc:2229 msgid "Brasero not installed" msgstr "Brasero no instalado" #: f.batch.cc:2318 f.widgets.cc:538 msgid "Find Duplicate Images" msgstr "Buscar imágenes duplicadas" #: f.batch.cc:2320 f.tools.cc:929 msgid "Thumbnail size" msgstr "Tamaño de miniatura" #: f.batch.cc:2323 msgid "pixel difference" msgstr "diferencia de píxels" #: f.batch.cc:2326 msgid "pixel count" msgstr "conteo de píxels" #: f.batch.cc:2329 msgid "Images:" msgstr "Imágenes:" #: f.batch.cc:2330 msgid "searching ..." msgstr "buscando ..." #: f.batch.cc:2332 msgid "Duplicates:" msgstr "Duplicados:" #: f.batch.cc:2350 #, c-format msgid "only %d image thumbnails found" msgstr "ercontradas sólo %d miniaturas de imágenes" #: f.bend.cc:86 f.widgets.cc:475 msgid "Unbend" msgstr "Enderezar" #: f.bend.cc:94 f.edit.cc:4267 f.effects.cc:4725 msgid "vertical" msgstr "vertical" #: f.bend.cc:95 f.edit.cc:4266 f.effects.cc:4721 msgid "horizontal" msgstr "horizontal" #: f.bend.cc:96 msgid "linear" msgstr "lineal" #: f.bend.cc:99 msgid "curved" msgstr "curvado" #: f.bend.cc:367 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.bend.cc:384 msgid "Perspective Correction" msgstr "Corregir perspectiva" #: f.bend.cc:590 msgid "must have 4 corners" msgstr "debe tener 4 esquinas" #: f.bend.cc:712 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.bend.cc:725 f.widgets.cc:477 msgid "Warp area" msgstr "Deformar área" #: f.bend.cc:730 msgid "start warp" msgstr "comenzar deformación" #: f.bend.cc:788 msgid "no active Select Area" msgstr "selección de área no activada" #: f.bend.cc:1129 f.bend.cc:1443 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.bend.cc:1147 f.widgets.cc:478 msgid "Warp curved" msgstr "Deformación curva" #: f.bend.cc:1156 f.mashup.cc:1836 msgid "warp span" msgstr "intervalo de deformación" #: f.bend.cc:1461 f.widgets.cc:479 msgid "Warp linear" msgstr "Deformación lineal" #: f.bend.cc:1776 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.bend.cc:1792 f.widgets.cc:480 msgid "Warp affine" msgstr "Deformación afín" #: f.bend.cc:2138 msgid "Flatten Book Page Photo" msgstr "Aplanar foto de página impresa" #: f.bend.cc:2139 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.bend.cc:2142 msgid "Stretch curved-down surfaces:" msgstr "Enderezar superficies curvadas por abajo" #: f.bend.cc:2197 msgid "top:" msgstr "arriba:" #: f.bend.cc:2200 msgid "bottom:" msgstr "abajo" #: f.combine.cc:2108 f.combine.cc:2740 f.combine.cc:3396 f.combine.cc:3928 msgid "Select 2 to 9 files" msgstr "Seleccione de 2 a 9 imágenes" #: f.combine.cc:2129 f.combine.cc:2761 f.combine.cc:3417 f.combine.cc:3949 msgid "Images are not all the same size" msgstr "No todas las imágenes son del mismo tamaño" #: f.combine.cc:2473 msgid "Adjust Image Contributions" msgstr "Ajustar aportes de imagen" #: f.combine.cc:2477 msgid "dark pixels" msgstr "sombras" #: f.combine.cc:2479 msgid "light pixels" msgstr "luces" #: f.combine.cc:2481 msgid "file:" msgstr "archivo:" #: f.combine.cc:2997 msgid "Paint and Warp Image" msgstr "Pintar y deformar imagen" #: f.combine.cc:3001 f.combine.cc:3646 f.combine.cc:6237 f.effects.cc:3912 #: f.widgets.cc:567 f.widgets.cc:590 f.widgets.cc:612 msgid "Image" msgstr "Imagen" #: f.combine.cc:3005 msgid "paint" msgstr "pintar" #: f.combine.cc:3006 msgid "warp" msgstr "deformar" #: f.combine.cc:3644 msgid "Select and Paint Image" msgstr "Seleccionar y pintar imagen" #: f.combine.cc:4120 msgid "Adjust Pixel Composition" msgstr "Ajustar composición de píxeles" #: f.combine.cc:4122 msgid "use average" msgstr "usar media" #: f.combine.cc:4123 msgid "use median" msgstr "usar mediana" #: f.combine.cc:4125 msgid "omit low pixel" msgstr "omitir píxeles bajos" #: f.combine.cc:4126 msgid "omit high pixel" msgstr "omitir píxeles altos" #: f.combine.cc:4388 f.combine.cc:5494 msgid "Select 2 to 4 files" msgstr "Seleccionar de 2 a 4 imagenes" #: f.combine.cc:4463 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:4465 f.combine.cc:5571 msgid "no curve (scanned image)" msgstr "sin curvatura (imagen escaneada)" #: f.combine.cc:4466 f.combine.cc:5572 msgid "Search for lens mm" msgstr "Buscar por longitud focal (mm)" #: f.combine.cc:4467 f.combine.cc:5573 msgid "Save lens mm → image EXIF" msgstr "Guardar focal en EXIF" #: f.combine.cc:4528 f.combine.cc:5634 msgid "Pre-align Images" msgstr "Pre-alinear imágenes" #: f.combine.cc:4532 f.combine.cc:5638 msgid "lens mm" msgstr "longitud focal (mm)" #: f.combine.cc:4537 f.combine.cc:5643 msgid "no auto warp" msgstr "no auto corrección" #: f.combine.cc:4539 f.combine.cc:5645 f.widgets.cc:445 f.widgets.cc:683 msgid "Resize" msgstr "Redimensionar" #: f.combine.cc:4540 f.combine.cc:5646 msgid "resize window" msgstr "redimensionar ventana" #: f.combine.cc:4548 f.combine.cc:5654 msgid "do not warp images during auto-alignment" msgstr "no deformar imágenes durante el auto alineado" #: f.combine.cc:4593 f.combine.cc:5699 msgid "use two images only" msgstr "usar sólo dos imágenes" #: f.combine.cc:4621 f.combine.cc:4825 f.combine.cc:5011 f.combine.cc:5725 #: f.combine.cc:5929 f.combine.cc:6115 msgid "Too little overlap, cannot align" msgstr "Solapamiento demasiado pequeño, no puedo alinear" #: f.combine.cc:5089 f.combine.cc:6193 msgid "Match Brightness and Color" msgstr "Concordancia de brillo y color" #: f.combine.cc:5134 msgid "Select image" msgstr "Seleccionar imagen" #: f.combine.cc:5149 f.combine.cc:6252 msgid "auto color" msgstr "color automático" #: f.combine.cc:5150 f.combine.cc:6253 msgid "file color" msgstr "archivo de color" #: f.combine.cc:5156 f.combine.cc:6259 msgid "mouse warp" msgstr "corregir con ratón" #: f.combine.cc:5159 msgid "curved image" msgstr "imagen curva" #: f.combine.cc:5569 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:6469 msgid "pano tools (hugin) not installed" msgstr "Pano Tools (Hugin) no instalado" #: f.combine.cc:6478 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:173 f.widgets.cc:437 f.widgets.cc:682 msgid "Trim/Rotate" msgstr "Recortar/Girar" #: f.edit.cc:185 f.edit.cc:586 msgid "ratio" msgstr "relación" #: f.edit.cc:189 msgid "trim size:" msgstr "tamaño de recorte:" #: f.edit.cc:194 msgid "Lock Ratio" msgstr "Bloquear la relación" #: f.edit.cc:199 msgid "Customize" msgstr "Personalizar" #: f.edit.cc:207 msgid "Rotate: degrees" msgstr "Girar : grados" #: f.edit.cc:209 msgid "auto-trim" msgstr "recorte automático" #: f.edit.cc:582 msgid "Trim Buttons" msgstr "Botones de recorte" #: f.edit.cc:585 msgid "label" msgstr "etiqueta" #: f.edit.cc:1411 msgid "Upright Image" msgstr "Enderezar imagen" #: f.edit.cc:1477 msgid "rotation unknown" msgstr "giro desconocido" #: f.edit.cc:2029 f.widgets.cc:441 f.widgets.cc:686 msgid "Retouch Combo" msgstr "Retocar luz y color" #: f.edit.cc:2050 msgid "Amplifier" msgstr "Amplificar" #: f.edit.cc:2051 fotoxx.h:1091 msgid "Brightness" msgstr "Brillo" #: f.edit.cc:2052 f.effects.cc:3364 fotoxx.h:1099 msgid "Contrast" msgstr "Contraste" #: f.edit.cc:2053 msgid "Low Color" msgstr "Color" #: f.edit.cc:2054 msgid "Warmer" msgstr "Calidez" #: f.edit.cc:2055 f.effects.cc:1011 msgid "Dark Areas" msgstr "Sombras" #: f.edit.cc:2064 msgid "Max." msgstr "Max" #: f.edit.cc:2065 f.edit.cc:2066 f.edit.cc:2067 msgid "High" msgstr "+" #: f.edit.cc:2068 msgid "Cooler" msgstr "Frio" #: f.edit.cc:2069 msgid "Bright" msgstr "Luces" #: f.edit.cc:2072 f.tools.cc:1631 msgid "Brightness Distribution" msgstr "Distribución del brillo" #: f.edit.cc:2075 msgid "Click for white balance or black level" msgstr "Clic para balance de blancos o punto negro" #: f.edit.cc:2078 msgid "Settings File" msgstr "Ajustes" #: f.edit.cc:2082 msgid "recall previous settings used" msgstr "recuperar ajustes previamente usados" #: f.edit.cc:2726 msgid "Adjust Brightness Distribution" msgstr "Ajustar histograma" #: f.edit.cc:2765 msgid "Low Cutoff" msgstr "Corte bajo" #: f.edit.cc:2766 msgid "High Cutoff" msgstr "Corte alto" #: f.edit.cc:2767 msgid "Low Flatten" msgstr "Aplanado bajo" #: f.edit.cc:2768 msgid "Mid Flatten" msgstr "Aplanado medio" #: f.edit.cc:2769 msgid "High Flatten" msgstr "Aplanado alto" #: f.edit.cc:2770 msgid "Low Stretch" msgstr "Tramo bajo" #: f.edit.cc:2771 msgid "Mid Stretch" msgstr "Tramo medio" #: f.edit.cc:2772 msgid "High Stretch" msgstr "Tramo alto" #: f.edit.cc:3102 msgid "Zonal Flatten Brightness" msgstr "Aplanar brillo zonal" #: f.edit.cc:3135 msgid "Zones" msgstr "Zonas" #: f.edit.cc:3142 msgid "Deband Dark" msgstr "Eliminar bandas oscuras" #: f.edit.cc:3145 msgid "Deband Bright" msgstr "Eliminar bandas brillantes" #: f.edit.cc:3594 f.widgets.cc:444 f.widgets.cc:689 msgid "Tone Mapping" msgstr "Mapeo de tonos" #: f.edit.cc:3625 msgid "low" msgstr "bajo" #: f.edit.cc:3627 msgid "high" msgstr "alto" #: f.edit.cc:3630 msgid "Amplify" msgstr "Ampliar" #: f.edit.cc:4034 msgid "Resize Image" msgstr "Redimensionar imagen" #: f.edit.cc:4059 msgid "W/H Ratio:" msgstr "Relación ancho/alto" #: f.edit.cc:4061 msgid "Lock" msgstr "Bloquear" #: f.edit.cc:4063 msgid "use previous settings" msgstr "usar ajustes anteriores" #: f.edit.cc:4262 f.widgets.cc:446 msgid "Flip" msgstr "Voltear" #: f.edit.cc:4368 msgid "+Version" msgstr "+Version" #: f.edit.cc:4391 msgid "Write Text on Image" msgstr "Escribir un texto sobre la imagen" #: f.edit.cc:4392 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:4441 f.mashup.cc:2437 msgid "Text" msgstr "Texto" #: f.edit.cc:4446 msgid "Use metadata key" msgstr "Usar clave de metadatos" #: f.edit.cc:4451 msgid "Use text file" msgstr "Usar archivo e texto" #: f.edit.cc:4470 f.mashup.cc:2455 msgid "text" msgstr "texto" #: f.edit.cc:4471 f.edit.cc:5309 f.mashup.cc:2456 f.mashup.cc:2798 msgid "backing" msgstr "fondo" #: f.edit.cc:4472 f.edit.cc:5310 f.mashup.cc:2457 f.mashup.cc:2799 msgid "outline" msgstr "contorno" #: f.edit.cc:4473 f.edit.cc:5311 f.mashup.cc:2458 f.mashup.cc:2800 msgid "shadow" msgstr "sombra" #: f.edit.cc:4499 msgid "save to current file" msgstr "guardar en archivo actual" #: f.edit.cc:4500 f.file.cc:1909 msgid "save as new file version" msgstr "guardar como una nueva versión de archivo" #: f.edit.cc:4501 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:4662 f.mashup.cc:2557 f.tools.cc:1068 msgid "select font" msgstr "seleccionar tipo de letra" #: f.edit.cc:4926 msgid "text file is defective" msgstr "archivo de texto defectuoso" #: f.edit.cc:5249 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:5280 msgid "Write Line or Arrow on Image" msgstr "Dibujar línea o flecha en la imagen" #: f.edit.cc:5288 f.mashup.cc:2777 msgid "Line length" msgstr "Longitud de línea" #: f.edit.cc:5295 f.mashup.cc:2784 msgid "Arrow head" msgstr "Punta de flecha" #: f.edit.cc:5308 f.mashup.cc:2797 msgid "line" msgstr "linea" #: f.edit.cc:5337 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:5926 f.widgets.cc:449 msgid "Paint Edits" msgstr "Editar pintando" #: f.edit.cc:5933 f.edit.cc:6162 fotoxx-15.11.cc:3179 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "No se puede conservar la selección de área.\n" "¿Continuar" #: f.edit.cc:5941 f.edit.cc:6170 f.tools.cc:2157 msgid "Edit function must be active" msgstr "la función de edición debe estar activa" #: f.edit.cc:5946 msgid "Cannot use Paint Edits" msgstr "No se puede usar Editar pintando" #: f.edit.cc:5975 msgid "power: center" msgstr "fuerza: centro" #: f.edit.cc:5980 msgid "reset area" msgstr "restablecer área" #: f.edit.cc:6155 f.widgets.cc:450 msgid "Leverage Edits" msgstr "Editar por niveles" #: f.edit.cc:6156 msgid "Edit Function Amplifier" msgstr "Amplificador de la función de edición" #: f.edit.cc:6175 msgid "Cannot use Leverage Edits" msgstr "No se puede usar Edición por niveles" #: f.edit.cc:6208 msgid "minimum" msgstr "sombras" #: f.edit.cc:6210 msgid "maximum" msgstr "luces" #: f.edit.cc:6510 msgid "Edit Plugins" msgstr "Editar complementos (plugins)" #: f.edit.cc:6511 msgid "Edit plugins menu" msgstr "Editar menú de plugins" #: f.edit.cc:6519 msgid "Run as Fotoxx edit function" msgstr "Abrir como una función de edición de Fotoxx" #: f.edit.cc:6757 msgid "Plugin working ..." msgstr "Plugin trabajando ..." #: f.edit.cc:6766 msgid "plugin failed" msgstr "falló el complemento" #: f.effects.cc:64 msgid "Set color depth to 1-16 bits" msgstr "Establecer profundidad de color 1-16 bits" #: f.effects.cc:76 msgid "Set Color Depth" msgstr "Ajustar profundidad de color" #: f.effects.cc:220 msgid "Convert to Sketch" msgstr "Convertir a esbozo" #: f.effects.cc:296 msgid "Clip Level" msgstr "Nivel de corte" #: f.effects.cc:300 msgid "Algorithm" msgstr "Algoritmo" #: f.effects.cc:305 msgid "Foreground" msgstr "Primer plano" #: f.effects.cc:308 msgid "Background" msgstr "Fondo" #: f.effects.cc:714 f.widgets.cc:486 msgid "Line Drawing" msgstr "Dibujo con líneas" #: f.effects.cc:727 msgid "black/white" msgstr " negro/blanco" #: f.effects.cc:1005 f.widgets.cc:487 msgid "Color Drawing" msgstr "Dibujo con colores" #: f.effects.cc:1012 msgid "Bright Areas" msgstr "Luces" #: f.effects.cc:1266 f.widgets.cc:488 msgid "Graduated Blur" msgstr "Desenfoque graduado" #: f.effects.cc:1270 msgid "Contrast Limit" msgstr "Límite de contraste" #: f.effects.cc:1273 f.repair.cc:775 msgid "Blur Radius" msgstr "Radio de desenfoque" #: f.effects.cc:1487 msgid "Simulate Embossing" msgstr "Simular relieve" #: f.effects.cc:1504 msgid "depth" msgstr "profundidad" #: f.effects.cc:1706 msgid "Simulate Tiles" msgstr "Simular un mosaico" #: f.effects.cc:1710 msgid "tile size" msgstr "tamaño de baldosas" #: f.effects.cc:1713 msgid "tile gap" msgstr "separación de baldosas" #: f.effects.cc:1716 msgid "3D depth" msgstr "profundidad 3D" #: f.effects.cc:1940 msgid "Convert Image to Dots" msgstr "Convertir imagen a puntos" #: f.effects.cc:1944 msgid "dot size" msgstr "tamaño del punto" #: f.effects.cc:2169 msgid "Simulate Painting" msgstr "Simular una pintura" #: f.effects.cc:2173 msgid "color depth" msgstr "profundidad de color" #: f.effects.cc:2177 msgid "patch area goal" msgstr "tamaño del área de color " #: f.effects.cc:2181 msgid "req. color match" msgstr "correspondencia de color requerida" #: f.effects.cc:2185 msgid "borders" msgstr "bordes" #: f.effects.cc:2758 f.widgets.cc:493 msgid "Vignette" msgstr "Viñetado" #: f.effects.cc:3107 msgid "Add Texture" msgstr "Añadir textura" #: f.effects.cc:3324 msgid "Background Pattern" msgstr "Patrón de fondo" #: f.effects.cc:3328 msgid "Pattern File:" msgstr "Archivo de patrón" #: f.effects.cc:3333 msgid "Geometry" msgstr "Geometría" #: f.effects.cc:3334 msgid "Calculate" msgstr "Calcular" #: f.effects.cc:3336 msgid "Zoom" msgstr "Zoom" #: f.effects.cc:3347 f.widgets.cc:495 msgid "Pattern" msgstr "Patrón" #: f.effects.cc:3354 msgid "Overlap" msgstr "Solape" #: f.effects.cc:3361 msgid "Opacity" msgstr "Opacidad" #: f.effects.cc:3429 msgid "choose pattern tile" msgstr "elegir archivo patrón" #: f.effects.cc:3816 msgid "Create Mosaic" msgstr "Crear mosaico" #: f.effects.cc:3902 msgid "Tile" msgstr "Baldosa" #: f.effects.cc:3910 f.widgets.cc:490 msgid "Tiles" msgstr "Baldosas" #: f.effects.cc:3916 msgid "Tile blending" msgstr "Mezcla de baldosas" #: f.effects.cc:3996 #, c-format msgid "exceeded max. tiles: %d" msgstr "excedido max. baldosas: %d" #: f.effects.cc:4011 #, c-format msgid "only %d tile images found" msgstr "encontradas sólo %d imágenes de baldosas" #: f.effects.cc:4404 f.widgets.cc:497 msgid "Custom Kernel" msgstr "Matriz de convolución" #: f.effects.cc:4408 msgid "Kernel size" msgstr "Tamaño de matriz" #: f.effects.cc:4411 msgid "Divisor" msgstr "Divisor" #: f.effects.cc:4414 msgid "Data file" msgstr "Archivo de datos" #: f.effects.cc:4450 fotoxx-15.11.cc:3440 msgid "Load settings from file" msgstr "Cargar ajustes desde un archivo" #: f.effects.cc:4711 f.widgets.cc:498 msgid "Make Waves" msgstr "Ondas" #: f.effects.cc:4718 msgid "wavelength" msgstr "longitud de onda" #: f.effects.cc:4719 msgid "amplitude" msgstr "amplitud" #: f.effects.cc:4720 msgid "variance" msgstr "varianza" #: f.effects.cc:4731 msgid "perspective" msgstr "perspectiva" #: f.effects.cc:4905 msgid "Pull image using the mouse." msgstr "Empujar imagen usando el ratón" #: f.effects.cc:4926 f.widgets.cc:499 msgid "Directed Blur" msgstr "Desenfoque direccional" #: f.effects.cc:4930 msgid "blur span" msgstr "cantidad de desnfoque" #: f.effects.cc:4933 msgid "intensity" msgstr "intensidad" #: f.effects.cc:5099 f.widgets.cc:500 msgid "Spherical Projection" msgstr "Proyección esférica" #: f.effects.cc:5143 msgid "Magnify" msgstr "Aumentar" #: f.file.cc:186 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:203 f.widgets.cc:572 msgid "File" msgstr "Archivo" #: f.file.cc:357 msgid "UFraw not installed" msgstr "UFRaw no instalado" #: f.file.cc:373 f.widgets.cc:691 msgid "Open RAW file (ufraw)" msgstr "Abrir archivo RAW (ufraw)" #: f.file.cc:378 f.file.cc:449 msgid "RAW type not registered in User Settings" msgstr "Formato RAW no registrado en las preferencias de usuario" #: f.file.cc:444 f.widgets.cc:692 msgid "Open RAW file (Raw Therapee)" msgstr "Abrir archivo RAW con RawTherapee" #: f.file.cc:541 f.widgets.cc:395 msgid "Open Image File" msgstr "Abrir archivo de imagen" #: f.file.cc:560 msgid "unknown file type" msgstr "tipo de archivo desconocido" #: f.file.cc:753 msgid "Create Blank Image" msgstr "Crear una imagen vacía" #: f.file.cc:755 msgid "file name" msgstr "nombre de archivo" #: f.file.cc:788 msgid "supply a file name" msgstr "proporcionar un nombre de archivo" #: f.file.cc:934 f.widgets.cc:400 msgid "Rename Image File" msgstr "Renombrar archivo de imagen" #: f.file.cc:941 msgid "Old Name" msgstr "nombre antiguo" #: f.file.cc:951 msgid "previous name" msgstr "nombre anterior" #: f.file.cc:952 msgid "add 1" msgstr "añadir 1" #: f.file.cc:1120 msgid "Copy Image File" msgstr "Copiar imagen" #: f.file.cc:1121 msgid "Move Image File" msgstr "Mover imagen" #: f.file.cc:1164 msgid "new location" msgstr "nueva ubicación" #: f.file.cc:1236 msgid "new location is not a directory" msgstr "la nueva ubicación no es un directorio" #: f.file.cc:1276 f.file.cc:1518 f.file.cc:1656 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "fallo al borrar: \n" " %s" #: f.file.cc:1363 f.widgets.cc:401 msgid "Trash Image File" msgstr "Enviar imagen a la papelera" #: f.file.cc:1364 f.file.cc:1571 msgid "(automatic step to next image)" msgstr "(pase automático a la nueva imagen)" #: f.file.cc:1432 msgid "" "Linux standard trash does not work. \n" "Desktop trash folder will be created." msgstr "" "La papelera estándar de Linux no funciona. \n" "Se creará una carpeta de papelera en el escritorio." #: f.file.cc:1435 msgid "" "Linux and Desktop trash do not work. \n" "Permanently delete the image file?" msgstr "" "Las papeleras de Linux y de escritorio no funcionan. \n" "¿Borrar permanentemente la imagen" #: f.file.cc:1438 #, c-format msgid "Cannot create trash folder: %s" msgstr "No se puede crear la carpeta de papelera: %s" #: f.file.cc:1450 msgid "Move read-only file to trash?" msgstr "¿Enviar el archivo de solo lectura a la papelera?" #: f.file.cc:1505 #, c-format msgid "error: %s" msgstr "error: %s" #: f.file.cc:1534 f.file.cc:1673 f.file.cc:2508 msgid "no more images" msgstr "no hay más imágenes" #: f.file.cc:1570 msgid "Delete Image File - CANNOT BE REVERSED" msgstr "Borrar archivo de imagen - NO SE PUEDE REVERTIR" #: f.file.cc:1646 msgid "Delete read-only file?" msgstr "¿Borrar archivo de sólo lectura?" #: f.file.cc:1891 msgid "Save Image File" msgstr "Guardar imagen" #: f.file.cc:1901 msgid "new version" msgstr "nueva versión" #: f.file.cc:1902 msgid "new file" msgstr "nuevo archivo" #: f.file.cc:1903 msgid "replace file" msgstr "reemplazar archivo" #: f.file.cc:1910 f.file.cc:2281 msgid "save as new file name or type" msgstr "guardar como un nuevo nombre o tipo de archivo" #: f.file.cc:1912 msgid "replace old file (OVERWRITE)" msgstr "reemplazar archivo antiguo (SOBREESCRIBIR)" #: f.file.cc:1950 f.file.cc:2057 msgid "cannot save as RAW type" msgstr "no se puede guardar como RAW" #: f.file.cc:2051 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:2191 msgid "Unable to copy EXIF/IPTC data" msgstr "No se pueden copiar los datos EXIF/IPTC" #: f.file.cc:2299 f.file.cc:2302 msgid "make current" msgstr "hacer actual" #: f.file.cc:2300 msgid "(new file becomes current file)" msgstr "(nuevo archivo será el archivo actual)" #: f.file.cc:2409 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "¿Sobreescribir archivo? \n" " %s" #: f.file.cc:2555 f.widgets.cc:554 msgid "Quick Start" msgstr "Referencia rápida" #: f.file.cc:2558 f.widgets.cc:555 msgid "User Guide" msgstr "Guia de usuario" #: f.file.cc:2561 f.widgets.cc:556 msgid "User Guide Changes" msgstr "Cambios en la guía de usuario" #: f.file.cc:2564 f.widgets.cc:558 msgid "README" msgstr "LÉAME" #: f.file.cc:2567 f.widgets.cc:557 msgid "Edit Functions Summary" msgstr "Resumen de las funciones de edición" #: f.file.cc:2570 f.widgets.cc:559 msgid "Change Log" msgstr "Registro de cambios" #: f.file.cc:2573 f.widgets.cc:560 msgid "Log File" msgstr "Archivo de registro" #: f.file.cc:2576 f.widgets.cc:561 msgid "Translations" msgstr "Traducciones" #: f.file.cc:2579 f.widgets.cc:562 msgid "Home Page" msgstr "Página web" #: f.file.cc:2582 f.widgets.cc:563 msgid "About" msgstr "Acerca de" #: f.file.cc:2586 f.widgets.cc:584 f.widgets.cc:606 fotoxx.h:1123 msgid "Help" msgstr "Ayuda" #: f.file.cc:2739 f.file.cc:2808 #, c-format msgid "file not found: %s" msgstr "archivo no encontrado: %s" #: f.file.cc:2745 f.file.cc:2814 #, c-format msgid "file type not supported: %s" msgstr "tipo de archivo no soportado: %s" #: f.gallery.cc:918 f.gallery.cc:1007 f.widgets.cc:604 msgid "Scroll" msgstr "Desplazar" #: f.gallery.cc:922 msgid "Sync" msgstr "Sync" #: f.gallery.cc:932 f.repair.cc:5845 f.repair.cc:5850 fotoxx.h:1144 msgid "Open" msgstr "Abrir" #: f.gallery.cc:933 msgid "change directory" msgstr "cambiar directorio" #: f.gallery.cc:941 msgid "GoTo" msgstr "Ir a" #: f.gallery.cc:947 f.widgets.cc:599 msgid "Sort" msgstr "Ordenar" #: f.gallery.cc:953 f.gallery.cc:1517 f.gallery.cc:1518 f.gallery.cc:1520 #: f.widgets.cc:597 msgid "Zoom+" msgstr "Zoom +" #: f.gallery.cc:963 f.gallery.cc:1519 f.gallery.cc:1521 f.widgets.cc:598 msgid "Zoom-" msgstr "Zoom -" #: f.gallery.cc:975 f.gallery.cc:1525 msgid "Row↑" msgstr "Fila ↑" #: f.gallery.cc:983 f.gallery.cc:1526 msgid "Row↓" msgstr "Fila ↓" #: f.gallery.cc:991 f.gallery.cc:1523 f.gallery.cc:1530 f.widgets.cc:602 msgid "Page↑" msgstr "Página ↑" #: f.gallery.cc:999 f.gallery.cc:1524 f.gallery.cc:1531 f.widgets.cc:603 msgid "Page↓" msgstr "Página ↓" #: f.gallery.cc:1013 f.gallery.cc:1528 f.widgets.cc:600 msgid "First" msgstr "Primera" #: f.gallery.cc:1014 f.gallery.cc:1529 f.widgets.cc:601 msgid "Last" msgstr "Última" #: f.gallery.cc:1127 msgid "Choose image directory" msgstr "Elegir directorio de imagen" #: f.gallery.cc:1134 f.gallery.cc:1148 msgid "recent" msgstr "reciente" #: f.gallery.cc:1135 f.gallery.cc:1153 msgid "newest" msgstr "más reciente" #: f.gallery.cc:1217 msgid "no albums found" msgstr "no se han encontrado albums" #: f.gallery.cc:1224 msgid "Choose album" msgstr "Elegir álbum" #: f.gallery.cc:1274 msgid "Gallery Sort" msgstr "Ordenar galería " #: f.gallery.cc:1278 msgid "File Name" msgstr "Nombre de archivo" #: f.gallery.cc:1279 msgid "File Mod Date/Time" msgstr "Modo de archivo Fecha/Hora" #: f.gallery.cc:1280 msgid "Photo Date/Time (EXIF)" msgstr "Fecha/Hora de la foto (EXIF)" #: f.gallery.cc:1282 msgid "ascending" msgstr "ascendente" #: f.gallery.cc:1283 msgid "descending" msgstr "descendente" #: f.gallery.cc:2212 fotoxx.h:1169 msgid "Select Files" msgstr "Seleccionar archivos" #: f.gallery.cc:2672 msgid "Click list position. Click thumbnail to add." msgstr "Clic en una posición de la lista. Clic en la miniatura para añadir" #: f.gallery.cc:2698 f.gallery.cc:2946 msgid "Edit Bookmarks" msgstr "Añadir marcadores" #: f.gallery.cc:2873 msgid "unable to save bookmarks file" msgstr "incapaz de guardar archivo de marcadores" #: f.gallery.cc:2946 msgid "Go To Bookmark" msgstr "Ir a un marcador" #: f.gallery.cc:3003 f.meta.cc:2844 f.repair.cc:7473 msgid "file not found" msgstr "archivo no encontrado" #: 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:241 msgid "choose layout file" msgstr "escoger archivo de capa" #: 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:417 msgid "Edit Images" msgstr "Editar imágenes" #: f.mashup.cc:418 f.mashup.cc:2432 msgid "Edit Text" msgstr "Editar texto" #: f.mashup.cc:419 msgid "Edit Line" msgstr "Editar línea" #: f.mashup.cc:420 msgid "Rescale" msgstr "Reescalar" #: f.mashup.cc:423 msgid "add or edit images" msgstr "añadir o editar imágenes" #: f.mashup.cc:424 msgid "add or edit text" msgstr "añadir o editar texto" #: f.mashup.cc:425 msgid "add or edit lines/arrows" msgstr "añadir o editar líneas/flechas" #: f.mashup.cc:426 msgid "change project scale" msgstr "cambiar escala del proyecto" #: f.mashup.cc:427 msgid "project complete" msgstr "proyecto completo" #: f.mashup.cc:428 msgid "cancel project" msgstr "cancelar proyecto" #: f.mashup.cc:446 msgid "rescale project" msgstr "reescalar proyecto" #: f.mashup.cc:501 msgid "save Mashup output file" msgstr "guardar archivo de salida del fotomontaje" #: f.mashup.cc:512 msgid "save Mashup project file?" msgstr "guardar archivo de proyecto del fotomontaje" #: f.mashup.cc:524 msgid "delete Mashup project file?" msgstr "¿borrar archivo de proyecto del fotomontaje?" #: f.mashup.cc:583 msgid "Open Project" msgstr "Abrir proyecto" #: f.mashup.cc:612 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "necesaria imagen de capa \n" " %s" #: f.mashup.cc:669 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "necesaria imagen superpuesta \n" " %s" #: f.mashup.cc:974 msgid "project file is defective" msgstr "archivo de proyecto defectuoso" #: f.mashup.cc:1004 msgid "Save Project" msgstr "Guardar proyecto" #: f.mashup.cc:1145 msgid "layout exceeds 2 gigabytes" msgstr "la composición excede de 2 gigabytes" #: f.mashup.cc:1219 msgid "Click image to select, drag image to move." msgstr "Clic en imagen para seleccionar, arrastrar para mover" #: f.mashup.cc:1220 msgid "Make black margins transparent" msgstr "Hacer transparentes los márgenes negros" #: f.mashup.cc:1259 msgid "Current image:" msgstr "Imagen activa" #: f.mashup.cc:1263 msgid "Cycle through images:" msgstr "Recorrido por las imágenes" #: f.mashup.cc:1270 msgid "Scale" msgstr "Escalar" #: f.mashup.cc:1279 msgid "Stacking Order" msgstr "Orden de pila" #: f.mashup.cc:1280 msgid "raise" msgstr "subir" #: f.mashup.cc:1281 msgid "lower" msgstr "bajar" #: f.mashup.cc:1284 msgid "Base Transparency" msgstr "Transparencia" #: f.mashup.cc:1288 msgid "Var. Transparency" msgstr "Variar transparencia" #: f.mashup.cc:1289 msgid "Paint" msgstr "Pintar" #: f.mashup.cc:1292 msgid "Bend and fine-align" msgstr "Mezclado y alineado fino" #: f.mashup.cc:1293 msgid "Warp" msgstr "Deformación" #: f.mashup.cc:1302 msgid "Margins" msgstr "Márgenes" #: f.mashup.cc:1303 msgid "Hard" msgstr "Fuerte" #: f.mashup.cc:1304 f.repair.cc:5441 msgid "Blend" msgstr "Mezclar" #: f.mashup.cc:1318 msgid "add images to layout" msgstr "añadir imágenes a la capa" #: f.mashup.cc:1557 msgid "Paint Image Transparencies" msgstr "Pintar transparencias de la imagen" #: f.mashup.cc:1575 msgid "Gradual" msgstr "Gradual" #: f.mashup.cc:1580 fotoxx.h:1148 msgid "Power" msgstr "Fuerza" #: f.mashup.cc:1814 msgid "Pull on the image with the mouse." msgstr "Empujar la imagen con el ratón" #: f.mashup.cc:1830 msgid "Warp Image" msgstr "Deformar imagen" #: f.mashup.cc:2263 #, c-format msgid "exceeded %d images" msgstr "excedidas en %d imágenes" #: f.mashup.cc:2405 msgid "Enter text, [Add] to layout, edit properties." msgstr "Introducir texto, [Añadir] a la capa, editar propiedades" #: f.mashup.cc:2485 msgid "Text File:" msgstr "Archivo de texto:" #: f.mashup.cc:2489 msgid "add entered text to layout" msgstr "añadir texto a la capa" #: f.mashup.cc:2618 msgid "click position to add text" msgstr "clicar en la posición para añadir texto" #: f.mashup.cc:2624 #, c-format msgid "exceeded %d text entries" msgstr "Excedidas en %d entradas de texto" #: f.mashup.cc:2747 msgid "Set line properties, [Add] to layout, edit." msgstr "Ajustar propiedades de línea, [Añadir] a la capa, editar." #: f.mashup.cc:2771 msgid "Edit Line/Arrow" msgstr "Editar línea/flecha" #: f.mashup.cc:2826 msgid "add line/arrow to layout" msgstr "añadir línea/flecha a la capa" #: f.mashup.cc:2919 msgid "click position to add line" msgstr "clic en la posició per afegir línia" #: f.mashup.cc:2925 #, c-format msgid "exceeded %d line entries" msgstr "excedidas %d entradas de línea" #: f.meta.cc:180 f.widgets.cc:669 msgid "View Metadata" msgstr "Ver metadatos" #: f.meta.cc:430 f.meta.cc:1807 f.widgets.cc:412 f.widgets.cc:670 msgid "Edit Metadata" msgstr "Editar metadatos" #: f.meta.cc:433 f.meta.cc:3819 msgid "save metadata to file" msgstr "guardar metadatos en el archivo" #: f.meta.cc:442 msgid "Image Date" msgstr "Fecha de la imagen" #: f.meta.cc:445 msgid "Time" msgstr "Hora" #: f.meta.cc:451 msgid "Rating (stars):" msgstr "Calificación (estrellas)" #: f.meta.cc:463 msgid "Caption" msgstr "Título" #: f.meta.cc:469 msgid "Comments" msgstr "Comentarios" #: f.meta.cc:477 msgid "Enter New Tag" msgstr "Introduzca nueva etiqueta" #: f.meta.cc:484 f.meta.cc:5003 msgid "Matching Tags" msgstr "Construyendo etiquetas" #: f.meta.cc:492 msgid "Image Tags" msgstr "Etiquetas de la imagen" #: f.meta.cc:498 msgid "Recent Tags" msgstr "etiquetas recientes" #: f.meta.cc:507 f.meta.cc:2029 f.meta.cc:5010 msgid "Defined Tags Category" msgstr "Categoría de etiquetas definida" #: f.meta.cc:898 msgid "date format is YYYY-MM-DD" msgstr "Formato de fecha es AAAA-MM-DD" #: f.meta.cc:902 msgid "date is invalid" msgstr "fecha no válida" #: f.meta.cc:936 msgid "time format is HH:MM [:SS]" msgstr "formato de hora es HH:MM [:SS]" #: f.meta.cc:940 msgid "time is invalid" msgstr "hora no válida" #: f.meta.cc:1025 fotoxx.h:1133 msgid "Manage Tags" msgstr "Administrar etiquetas" #: f.meta.cc:1025 msgid "orphan tags" msgstr "etiquetas huérfanas" #: f.meta.cc:1029 msgid "category" msgstr "categoria" #: f.meta.cc:1032 msgid "tag" msgstr "etiqueta" #: f.meta.cc:1039 msgid "Defined Tags:" msgstr "etiquetas definidas:" #: f.meta.cc:1811 f.meta.cc:2474 msgid "key name" msgstr "nombre clave" #: f.meta.cc:1813 f.meta.cc:2475 msgid "key value" msgstr "valor clave" #: f.meta.cc:1900 f.widgets.cc:414 msgid "Delete Metadata" msgstr "Borrar metadatos" #: f.meta.cc:1902 fotoxx.h:1082 msgid "All" msgstr "Todo" #: f.meta.cc:1903 msgid "One Key:" msgstr "Una clave:" #: f.meta.cc:2006 f.widgets.cc:415 f.widgets.cc:540 msgid "Batch Add/Remove Tags" msgstr "Añadir/eliminar etiquetas en lote" #: f.meta.cc:2017 msgid "tags to add" msgstr "etiquetas para añadir" #: f.meta.cc:2018 msgid "tags to remove" msgstr "etquetas para eliminar" #: f.meta.cc:2102 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " demasiadas etiquetas" #: f.meta.cc:2217 msgid "specify files and tags" msgstr "especificar archivos y etiquetas" #: f.meta.cc:2277 msgid "tag names file" msgstr "archgivo de nombres de etiqueta" #: f.meta.cc:2349 #, 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:2461 msgid "Batch Metadata" msgstr "Metadatos en lote" #: f.meta.cc:2468 msgid "Short List" msgstr "Lista reducida" #: f.meta.cc:2469 msgid "Full List" msgstr "Lista completa" #: f.meta.cc:2511 msgid "enter key names" msgstr "entrar nombres clave" #: f.meta.cc:2517 fotoxx.h:1141 msgid "no files selected" msgstr "no hay archivos seleccionados" #: f.meta.cc:2643 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:3276 f.meta.cc:4011 f.meta.cc:4277 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "latitud/longitud incorrecta: %s %s" #: f.meta.cc:3320 f.meta.cc:3682 f.meta.cc:4486 f.meta.cc:4894 msgid "-noindex in use, disabled" msgstr "-noindex en uso, ​deshabilitar" #: f.meta.cc:3336 msgid "choose map file" msgstr "elegir archivo de mapas" #: f.meta.cc:3444 msgid "" "fotoxx-maps package not installed \n" "(see http://kornelix.com/packages and /tarballs)" msgstr "" "no instalado paquete de mapas de fotox \n" "(ver http://kornelix.com/packages y /tarballs" #: f.meta.cc:3449 #, c-format msgid "map file %s is missing" msgstr "necesario archivo de mapas %s" #: f.meta.cc:3453 #, 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:3731 msgid "No matching images found" msgstr "No encontradas imagenes coincidentes" #: f.meta.cc:3771 msgid "search range (km)" msgstr "buscar en un rango (km)" #: f.meta.cc:3804 f.widgets.cc:418 f.widgets.cc:671 msgid "Edit Geotags" msgstr "Editar Geoetiquetas" #: f.meta.cc:3805 msgid "Geocoding web service courtesy of" msgstr "Servicio web de geocódigos cortesía de" #: f.meta.cc:3821 f.meta.cc:4060 f.meta.cc:5783 msgid "city" msgstr "ciudad" #: f.meta.cc:3824 f.meta.cc:4063 f.meta.cc:5786 msgid "country" msgstr "pais" #: f.meta.cc:3906 f.meta.cc:4194 msgid "city not found" msgstr "ciudad no encontrada" #: f.meta.cc:4027 f.widgets.cc:419 f.widgets.cc:543 msgid "Batch Add Geotags" msgstr "Añadir geoetiquetas en lote" #: f.meta.cc:4256 msgid "" "data is incomplete \n" " proceed?" msgstr "" "los datos están incompletos \n" " ¿proceder?" #: f.meta.cc:4313 msgid "choose city" msgstr "escoger ciudad" #: f.meta.cc:4400 msgid "not found" msgstr "no encontrada" #: f.meta.cc:4401 msgid "city and country required" msgstr "ciudad y pais requeridos" #: f.meta.cc:4501 msgid "Report Geotag Groups" msgstr "Informe de grupos de etiquetas" #: f.meta.cc:4502 msgid "Group by country" msgstr "Agrupar por país" #: f.meta.cc:4503 msgid "Group by country/city" msgstr "Agrupar por país/ciudad" #: f.meta.cc:4504 msgid "Group by country/city/date" msgstr "Agrupar por país/ciudad/fecha" #: f.meta.cc:4507 msgid "Combine within" msgstr "Combinar dentro" #: f.meta.cc:4509 msgid "days" msgstr "dias" #: f.meta.cc:4625 msgid "geotag groups" msgstr "grupos de geoetiquetas" #: f.meta.cc:4931 msgid "Search Image Metadata" msgstr "Buscar los metadatos de la imagen" #: f.meta.cc:4935 msgid "images to search:" msgstr "imágenes a buscar:" #: f.meta.cc:4936 msgid "all" msgstr "todo" #: f.meta.cc:4937 msgid "current set only" msgstr "sólo selección actual" #: f.meta.cc:4940 msgid "matching images:" msgstr "imágenes coincidentes" #: f.meta.cc:4941 msgid "new set" msgstr "nueva selección" #: f.meta.cc:4942 msgid "add to set" msgstr "añadir a la selección" #: f.meta.cc:4943 msgid "remove" msgstr "eliminar" #: f.meta.cc:4946 msgid "report type:" msgstr "tipo de imforme:" #: f.meta.cc:4947 msgid "gallery" msgstr "galería" #: f.meta.cc:4948 fotoxx-15.11.cc:791 msgid "metadata" msgstr "metadatos" #: f.meta.cc:4954 msgid "date range" msgstr "rango de fechas" #: f.meta.cc:4955 msgid "stars range" msgstr "rango de estrellas" #: f.meta.cc:4956 msgid "search tags" msgstr "buscar etiquetas" #: f.meta.cc:4957 msgid "search text" msgstr "buscar texto" #: f.meta.cc:4958 msgid "search files" msgstr "buscar archivos" #: f.meta.cc:4963 msgid "(yyyymmdd)" msgstr "(aaaammdd)" #: f.meta.cc:4968 msgid "last version only" msgstr "sólo última versión" #: f.meta.cc:4970 msgid "all/any" msgstr "todo/cualquiera" #: f.meta.cc:4989 msgid "other criteria" msgstr "otro criterio" #: f.meta.cc:4993 msgid "other" msgstr "otro" #: f.meta.cc:4999 msgid "Enter Search Tag" msgstr "Introducir etiqueta de búsqueda" #: f.meta.cc:5294 #, c-format msgid "not a defined tag: %s" msgstr "no está definida la etiqueta: %s" #: f.meta.cc:5325 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:5332 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:5376 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "búsqueda de datos no razonable \n" " %s %s" #: f.meta.cc:5394 msgid "stars range not reasonable" msgstr "rango de estrellas no razonable" #: f.meta.cc:5639 #, 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:5642 msgid "no changes made" msgstr "no se han efectuado cambios" #: f.meta.cc:5780 msgid "Add Geotags Search Criteria" msgstr "Añadir criterios de búsqueda de geoetiquetas" #: f.meta.cc:5799 msgid "range (km)" msgstr "rango (km)" #: f.meta.cc:5892 msgid "error in latitude/longitude/range" msgstr "error en latitud/longitud/rango" #: f.meta.cc:5956 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:5980 msgid "Additional Items for Report" msgstr "Elementos adicionales para el informe" #: f.meta.cc:5986 msgid "Keyword" msgstr "Palabra clave" #: f.meta.cc:5993 msgid "Match Criteria" msgstr "Criterio de coincidencia" #: f.meta.cc:6553 msgid "image index is missing" msgstr "necesario indexar imágenes" #: f.repair.cc:1041 msgid "Apply repeatedly while watching the image." msgstr "Aplicar repetidamente mientras se observe la imagen" #: f.repair.cc:1042 msgid "Measure" msgstr "Medida" #: f.repair.cc:1078 msgid "Noise Reduction" msgstr "Reducción de ruido" #: f.repair.cc:1099 msgid "Flatten 1" msgstr "Aplanar 1" #: f.repair.cc:1100 msgid "Flatten 2" msgstr "Aplanar 2" #: f.repair.cc:1101 msgid "Median" msgstr "Mediana" #: f.repair.cc:1121 msgid "dark areas" msgstr "áreas oscuras" #: f.repair.cc:1123 msgid "all areas" msgstr "todas las áreas" #: f.repair.cc:1246 msgid "Measure Noise" msgstr "Medir ruído" #: f.repair.cc:1247 msgid "Click on a monotone image area." msgstr "Clic en un área monotono de la imagen" #: f.repair.cc:2177 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:2199 f.widgets.cc:457 msgid "Smart Erase" msgstr "Borrado inteligente" #: f.repair.cc:2204 fotoxx.h:1152 msgid "Radius" msgstr "Radio" #: f.repair.cc:2206 f.widgets.cc:455 msgid "Blur" msgstr "Desenfocar" #: f.repair.cc:2209 msgid "New Area" msgstr "Nueva área" #: f.repair.cc:2558 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:2574 msgid "Red Eye Reduction" msgstr "Reducción de ojos rojos" #: f.repair.cc:3038 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" #: f.repair.cc:3070 f.widgets.cc:459 msgid "Paint/Clone" msgstr "Pintar/Clonar" #: f.repair.cc:3074 msgid "paint color" msgstr "color de la pintura" #: f.repair.cc:3077 msgid "copy from image" msgstr "copiar desde la imagen" #: f.repair.cc:3084 f.repair.cc:3641 msgid "paintbrush radius" msgstr "radio del pincel" #: f.repair.cc:3085 msgid "transparency center" msgstr "transparencia del centro" #: f.repair.cc:3086 msgid "transparency edge" msgstr "transparencia de los bordes" #: f.repair.cc:3093 f.repair.cc:3648 msgid "gradual paint" msgstr "pintado gradual" #: f.repair.cc:3100 #, c-format msgid "Undo Memory %d%c" msgstr "Memoria de deshacer %d%c" #: f.repair.cc:3436 msgid "" "Undo memory limit has been reached. \n" "Save work with [done], then resume painting." msgstr "" "Se alcanzó el límite de memoria para deshacer. \n" "Guarde el trabajo con [Hecho], y siga pintando." #: f.repair.cc:3601 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:3633 f.widgets.cc:460 msgid "Paint Transparency" msgstr "Pintar transparencia" #: f.repair.cc:3642 msgid "strength center" msgstr "fuerza en el centro" #: f.repair.cc:3643 msgid "strength edge" msgstr "fuerza en bordes" #: f.repair.cc:3858 f.widgets.cc:461 msgid "Color Mode" msgstr "Modo de color" #: f.repair.cc:3862 msgid "black/white positive" msgstr "positivo en blanco y negro" #: f.repair.cc:3863 msgid "black/white negative" msgstr "negativo en blanco y negro" #: f.repair.cc:3864 msgid "color positive" msgstr "Positivo en color" #: f.repair.cc:3865 msgid "color negative" msgstr "Negativo en color" #: f.repair.cc:3866 msgid "sepia" msgstr "sepia" #: f.repair.cc:4046 f.widgets.cc:462 msgid "Shift Colors" msgstr "Modificar colores" #: f.repair.cc:4317 msgid "+Brightness" msgstr "Brillo" #: f.repair.cc:4318 msgid "+Red -Cyan" msgstr "+Rojo -Cyan" #: f.repair.cc:4319 msgid "+Green -Magenta" msgstr "+Verde -Magenta" #: f.repair.cc:4320 msgid "+Blue -Yellow" msgstr "+Azul -Amarillo" #: f.repair.cc:4326 fotoxx.h:1154 msgid "Red" msgstr "R" #: f.repair.cc:4327 fotoxx.h:1120 msgid "Green" msgstr "V" #: f.repair.cc:4328 fotoxx.h:1089 msgid "Blue" msgstr "A" #: f.repair.cc:4623 msgid "Color to change" msgstr "Color a cambiar" #: f.repair.cc:4625 msgid "shift+click on image to select color" msgstr "Mayús+clic en la imagen para seleccionar color" #: f.repair.cc:4638 msgid "Color Hue" msgstr "Tono de color" #: f.repair.cc:4639 msgid "Saturation" msgstr "Saturación" #: f.repair.cc:4640 msgid "Lightness" msgstr "Luminosidad" #: f.repair.cc:4641 msgid "Color Match" msgstr "Coincidencia de color" #: f.repair.cc:4642 msgid "Adjustment" msgstr "Ajuste" #: f.repair.cc:5028 msgid "Ramp brightness across image" msgstr "Rampa de brillo a traves de la imagen" #: f.repair.cc:5352 f.tools.cc:2048 msgid "Click image to select pixels." msgstr "Pulsar en la imagen para seleccionar los píxeles." #: f.repair.cc:5383 f.widgets.cc:466 msgid "Color Ramp" msgstr "Rampa de color" #: f.repair.cc:5394 msgid "Metric:" msgstr "Métrica:" #: f.repair.cc:5817 msgid "Color Match Images" msgstr "Concordar color de imágenes" #: f.repair.cc:5843 msgid "mouse radius for color sample" msgstr "radio del ratón para muestra de color" #: f.repair.cc:5846 msgid "image for source color" msgstr "imagen para el color de origen" #: f.repair.cc:5848 msgid "click on image to get source color" msgstr "Pulse en la imagen para obtener color de origen" #: f.repair.cc:5851 msgid "image to set matching color" msgstr "imagen para establecer concordancia de color" #: f.repair.cc:5853 msgid "click on image to set matching color" msgstr "Pulse en la imagen para establecer la concordancia de color" #: f.repair.cc:5914 msgid "select source image color first" msgstr "seleccionar primero el color de la imagen de origen" #: f.repair.cc:6099 msgid "Change Color Profile" msgstr "Cambiar perfil de color" #: f.repair.cc:6103 msgid "input profile" msgstr "perfil de entrada" #: f.repair.cc:6107 msgid "output profile" msgstr "perfil de salida" #: f.repair.cc:6123 msgid "color profile" msgstr "perfil de color" #: f.repair.cc:6169 f.repair.cc:6175 #, c-format msgid "unknown cms profile %s" msgstr "perfil cms desconocido %s" #: f.repair.cc:6307 f.widgets.cc:469 msgid "Remove Dust" msgstr "Retirar polvo" #: f.repair.cc:6311 msgid "spot size limit" msgstr "límite de tamaño de punto" #: f.repair.cc:6314 msgid "max. brightness" msgstr "máximo brillo" #: f.repair.cc:6317 msgid "min. contrast" msgstr "mínimo contraste" #: f.repair.cc:7098 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:7121 f.widgets.cc:471 msgid "Color Fringes" msgstr "Bandas de color" #: f.repair.cc:7320 f.widgets.cc:472 msgid "Stuck Pixels" msgstr "Píxeles retenidos" #: f.repair.cc:7326 msgid "pixel group" msgstr "agrupar píxeles" #: f.repair.cc:7334 f.repair.cc:7402 msgid "stuck pixels:" msgstr "píxeles defectuosos" #: f.repair.cc:7430 msgid "Load Stuck Pixels" msgstr "Cargar píxeles defectuosos" #: f.repair.cc:7432 msgid "File:" msgstr "Archivo:" #: f.repair.cc:7457 f.repair.cc:7514 msgid "Stuck Pixels file" msgstr "Archivo de píxeles defectuosos" #: f.repair.cc:7494 msgid "file format error" msgstr "error de formato de archivo" #: f.tools.cc:70 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:72 msgid "Select to add, click on X to delete." msgstr "Seleccionar para añadir, clic en X para borrar." #: f.tools.cc:73 msgid "Select directory for thumbnails." msgstr "Seleccionar directorio para las miniaturas." #: f.tools.cc:74 msgid "" "Index function terminated. Fotoxx will close. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Función de indexado terminada. Fotoxx se cerrará. \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:106 f.widgets.cc:513 msgid "Index Image Files" msgstr "Indexar imágenes" #: f.tools.cc:250 msgid "Choose top image directories" msgstr "Seleccionar el directorio raiz de imágenes" #: f.tools.cc:251 msgid "Choose thumbnail directory" msgstr "Elegir directorio de miniaturas" #: f.tools.cc:324 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:330 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" "Directorio inválido : \n" " %s \n" "Por favor borrar." #: f.tools.cc:333 #, 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:336 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" "Directorio duplicado: \n" " %s \n" "Por favor borrar" #: f.tools.cc:385 msgid "no thumbnails directory defined" msgstr "no definido directorio de miniaturas" #: f.tools.cc:735 msgid "COMPLETED" msgstr "COMPLETADO" #: f.tools.cc:776 msgid "Indexing is required for first-time startup." msgstr "El indexado es necesario para el primer arranque" #: f.tools.cc:838 msgid "Recent Files Gallery" msgstr "Galería de archivos recientes" #: f.tools.cc:839 msgid "Newest Files Gallery" msgstr "Galería de archivos más recientes" #: f.tools.cc:840 msgid "Previous Gallery" msgstr "Galería previa" #: f.tools.cc:841 msgid "Previous Image" msgstr "Imagen previa" #: f.tools.cc:842 msgid "Blank Window" msgstr "Ventana vacía" #: f.tools.cc:843 msgid "Directory" msgstr "Directorio" #: f.tools.cc:844 msgid "Image File" msgstr "Archivo de imagen" #: f.tools.cc:887 f.widgets.cc:514 msgid "User Options" msgstr "Opciones de usuario" #: f.tools.cc:890 msgid "Startup Display" msgstr "Pantalla de inicio" #: f.tools.cc:901 msgid "Menu Style" msgstr "Estilo de menú" #: f.tools.cc:902 msgid "Icons" msgstr "Iconos" #: f.tools.cc:903 msgid "Icons + Text" msgstr "Iconos + texto" #: f.tools.cc:905 msgid "Icon size" msgstr "Tamaño de icono" #: f.tools.cc:909 msgid "Dialog font and size" msgstr "Diálogo fuente y tamaño" #: f.tools.cc:914 msgid "Image Pan/scroll:" msgstr "Fijar/deslizar imagen" #: f.tools.cc:917 msgid "Zooms for 2x" msgstr "Zooms x2" #: f.tools.cc:926 msgid "JPEG save quality" msgstr "calidad de guardado JPEG" #: f.tools.cc:933 msgid "Curve node capture distance" msgstr "distancia de captura de nodo de curva" #: f.tools.cc:937 msgid "show hidden directories in gallery view" msgstr "mostrar directorios ocultos en la vista de galería" #: f.tools.cc:940 msgid "prev/next shows last file version only" msgstr "ant/sig muestra sólo la última versión del archivo" #: f.tools.cc:943 msgid "RAW command" msgstr "Comando RAW" #: f.tools.cc:947 msgid "RAW file types" msgstr "Tipos de archivos RAW" #: f.tools.cc:1029 msgid "Delete present thumbnails to make effective" msgstr "Borrar la diapositiva actual para hacerlo efectivo" #: f.tools.cc:1051 msgid "Select startup directory" msgstr "Seleccionar el directorio de inicio" #: f.tools.cc:1058 msgid "Select startup image file" msgstr "Seleccionar el archivo de imagen de inicio" #: f.tools.cc:1096 msgid "startup directory is invalid" msgstr "El directorio de inicio no es correcto" #: f.tools.cc:1107 msgid "startup file is invalid" msgstr "El archivo de inicio no es correcto" #: f.tools.cc:1247 msgid "Edit KB Shortcuts" msgstr "Editar atajos de teclado" #: f.tools.cc:1252 msgid "shortcut key:" msgstr "atajo de teclado" #: f.tools.cc:1253 msgid "(enter key)" msgstr "(entrar tecla)" #: f.tools.cc:1379 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Reservada, no se puede utilizar" #: f.tools.cc:1533 msgid "unable to save KB-shortcuts file" msgstr "imposible guardar archivos de atajos de teclado" #: f.tools.cc:1837 f.widgets.cc:517 msgid "Grid Lines" msgstr "Lineas de rejilla" #: f.tools.cc:1846 msgid "x-spacing" msgstr "espaciado X" #: f.tools.cc:1847 msgid "x-count" msgstr "conteo X" #: f.tools.cc:1848 msgid "x-enable" msgstr "activar X" #: f.tools.cc:1854 msgid "y-spacing" msgstr "espaciado Y" #: f.tools.cc:1855 msgid "y-count" msgstr "conteo Y" #: f.tools.cc:1856 msgid "y-enable" msgstr "activar Y" #: f.tools.cc:1976 f.tools.cc:1981 f.widgets.cc:518 msgid "Line Color" msgstr "Color de línea" #: f.tools.cc:1978 msgid "Area Color" msgstr "Color de área" #: f.tools.cc:2083 f.widgets.cc:519 msgid "Show RGB" msgstr "Mostrar RGB" #: f.tools.cc:2375 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key M to toggle dialog." msgstr "" "Arrastrar el ratón sobre la imagen. \n" "Clic izquierdo para cancelar. \n" "Tecla M para esconder diálogo." #: f.tools.cc:2401 f.widgets.cc:520 msgid "Magnify Image" msgstr "Aumentar imagen" #: f.tools.cc:2410 msgid "X-size" msgstr "Lupa" #: f.tools.cc:2655 msgid "Darkest and Brightest Pixels" msgstr "Píxeles más oscuros y más brillantes" #: f.tools.cc:2678 msgid "Dark Limit" msgstr "Sombra" #: f.tools.cc:2679 msgid "Bright Limit" msgstr "Luz" #: f.tools.cc:2774 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:2870 f.widgets.cc:523 msgid "Monitor Gamma" msgstr "Gamma del monitor" #: f.tools.cc:2930 msgid "Available Translations" msgstr "Traducciones disponibles" #: f.tools.cc:2934 msgid "Set Language" msgstr "Seleccionar el idioma" #: f.tools.cc:3071 f.widgets.cc:526 msgid "Calibrate Printer" msgstr "Calibrar impresora" #: f.tools.cc:3096 msgid "print color chart" msgstr "imprimir carta de colores" #: f.tools.cc:3097 msgid "scan and save color chart" msgstr "escanear y guardar carta de colores" #: f.tools.cc:3098 msgid "align and trim color chart" msgstr "alinear y recortar carta de colores" #: f.tools.cc:3099 msgid "open and process color chart" msgstr "abrir y procesar carta de colores" #: f.tools.cc:3100 msgid "print image with revised colors" msgstr "imprimir imagen con colores revisados" #: f.tools.cc:3264 #, c-format msgid "" "Scan the printed color chart. \n" "Save in %s/" msgstr "" "Escanear la carta de colores impresa. \n" "Guardar en %s/" #: f.tools.cc:3278 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation caused by the scanner. \n" "Trim off all margins. Be careful not to cut off \n" "any color tile edges or leave any white margins." msgstr "" "Abrir y editar la carta de colores escaneada. \n" "Eliminar cualquier oblicuidad o rotación causada por el escaner. \n" "Recortar todos los márgenes. Ponga cuidado en no cortar \n" "ningún extremo del mosaico de color o dejar ningún margen." #: f.tools.cc:3310 msgid "Open the trimmed color chart file" msgstr "Abrir la carta de colores recortada" #: f.tools.cc:3443 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:3483 msgid "Color map file to use" msgstr "Archivo de mapa de color para usar" #: f.tools.cc:3500 msgid "Select the image file to print." msgstr "Seleccionar la imagen a imprimir" #: f.tools.cc:3565 msgid "converting colors..." msgstr "convirtiendo colores ..." #: f.tools.cc:3665 msgid "Image colors are converted for printing." msgstr "Colores de la imagen se han convertido para imprimir" #: f.widgets.cc:111 msgid "Album" msgstr "Álbum" #: f.widgets.cc:113 msgid "TOP" msgstr "ARRIBA" #: f.widgets.cc:165 msgid "Current Image File (key F)" msgstr "Archivo de imagen actual (tecla F)" #: f.widgets.cc:166 msgid "Thumbnail Gallery (key G)" msgstr "Galería de mi​niaturas (tecla G)" #: f.widgets.cc:167 msgid "World Maps (key W)" msgstr "Mapa mundial (tecla W)​" #: f.widgets.cc:170 msgid "Favorite Functions" msgstr "Funciones favoritas" #: f.widgets.cc:171 msgid "File: Open, RAW, Rename, Trash, Print" msgstr "Archivo: Abrir, RAW, Renombrar, Papelera, Imprimir" #: f.widgets.cc:172 msgid "Save modified image file to disk" msgstr "Guardar imagen modificada en el disco" #: f.widgets.cc:173 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:174 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "" "Metadatos: leyendas, Etiquetas, Valoraciones, Geoetiquetas, Búsqueda ..." #: f.widgets.cc:175 msgid "Areas: Select areas to edit, copy and paste" msgstr "Areas: Seleccionar áreas para editar, copiar y pegar" #: f.widgets.cc:176 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "Editar: Recortar, Girar,​ Brillo, Contraste, Texto ..." #: f.widgets.cc:177 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "Reparar: Enfocar, Ruido, Ojos rojos, Color, Pintar, Clonar ..." #: f.widgets.cc:178 msgid "Bend: Fix Perspective, Bend/Warp image ..." msgstr "Deformar: Corregir perspectiva, Deformar imágenes ..." #: f.widgets.cc:179 msgid "Effects: Special Effects, Arty Transforms" msgstr "Efectos: Efectos especiales, Transformaciones artísticas" #: f.widgets.cc:180 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "Combinar: HDR, HDF, Panorama, Pila, Montaje" #: f.widgets.cc:181 msgid "" "Undo or Redo one edit (left/right mouse click) \n" " hold key A to include all edits" msgstr "" "Deshacer o rehacer una edición (clic izquierdo/derecho del ratón)​ \n" " presionar tecla A para incluir todas las ediciones" #: f.widgets.cc:183 msgid "Tools: Index, Options, Shortcuts, Magnify ..." msgstr "Herramientas: Indexar, Opciones, Atajos, Aumentar ..." #: f.widgets.cc:184 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "Ayuda: Inicio rápido, Guía de usuario, Cambios recientes ..." #: f.widgets.cc:187 msgid "Set gallery from current image file" msgstr "Establecer galería desde la imagen actual" #: f.widgets.cc:188 msgid "Albums: Manage Albums, Slide Show, Desktop Wallpaper" msgstr "Álbumes: Administrar álbumes, Diaporama, Fondos de pantalla" #: f.widgets.cc:189 msgid "go to bookmarked image" msgstr "ir a una imagen marcada" #: f.widgets.cc:190 msgid "increase thumbnail size" msgstr "aumentar tamaño de miniatura" #: f.widgets.cc:191 msgid "reduce thumbnail size" msgstr "reducir tamaño de la miniatura" #: f.widgets.cc:192 msgid "change sort order" msgstr "cambiar criterio de ordenación" #: f.widgets.cc:193 msgid "jump to beginning (top)" msgstr "ir al principio (arriba)" #: f.widgets.cc:194 msgid "jump to end (bottom)" msgstr "ir al final (abajo)" #: f.widgets.cc:195 msgid "previous page" msgstr "página anterior" #: f.widgets.cc:196 msgid "next page" msgstr "página siguiente" #: f.widgets.cc:197 msgid "slow scroll" msgstr "desplazamiento lento" #: f.widgets.cc:198 msgid "Batch conversion, metadata, RAW processing" msgstr "Conversión en lote, metadatos, procesado RAW" #: f.widgets.cc:201 msgid "Choose a map for image locations" msgstr "Elegir un mapa para ubicar imagen" #: f.widgets.cc:202 msgid "Set image search radius for map click" msgstr "Ajustar radio de búsqueda de imagen para clicar en el mapa" #: f.widgets.cc:205 msgid "Open another window" msgstr "Abrir otra ventana" #: f.widgets.cc:206 msgid "Open a new image file" msgstr "Abrir nueva imagen" #: f.widgets.cc:207 msgid "Open the previously seen file" msgstr "Abrir archivo previamente visto" #: f.widgets.cc:208 msgid "Open a recently seen file" msgstr "Abrir archivo recientemente visto" #: f.widgets.cc:209 msgid "Open a newly added file" msgstr "Abrir un archivo añadido nuevo" #: f.widgets.cc:210 f.widgets.cc:211 msgid "Open and edit a camera RAW file" msgstr "Abrir y editar un archivo de cámara RAW" #: f.widgets.cc:212 msgid "Change the image file name" msgstr "Cambiar el nombre de la imagen" #: f.widgets.cc:213 msgid "Create a blank image" msgstr "Crear una imagen en blanco" #: f.widgets.cc:214 msgid "Move image file to Trash" msgstr "Mover imagen a la papelera" #: f.widgets.cc:215 msgid "Permanently delete image file" msgstr "Borrar imagen permanentemente " #: f.widgets.cc:216 msgid "Print the current image" msgstr "Imprimir la imagen actual" #: f.widgets.cc:217 msgid "Print current image with adjusted colors" msgstr "Imprimir imagen actual con los colores ajustados" #: f.widgets.cc:218 f.widgets.cc:406 msgid "Quit Fotoxx" msgstr "Salir de Fotoxx" #: f.widgets.cc:221 msgid "List a few key metadata items" msgstr "Listar algunos datos clave de metadatos" #: f.widgets.cc:222 msgid "List all metadata items" msgstr "Listar todos los items de metadatos" #: f.widgets.cc:223 msgid "(Toggle) show captions and comments" msgstr "(Cambiar) muestra leyendas y comentarios" #: f.widgets.cc:224 msgid "Edit image tags/caption/rating ..." msgstr "Editar etiquetas/leyenda/clasificación de la imagen ..." #: f.widgets.cc:225 msgid "Edit any image metadata" msgstr "Editar cualquier metadato de la imagen" #: f.widgets.cc:226 msgid "Remove all metadata from an image" msgstr "Quitar todos los metadatos de una imagen" #: f.widgets.cc:227 msgid "Add/remove tags for multiple images" msgstr "Añadir/eliminar etiquetas para múltiples imágenes " #: f.widgets.cc:228 msgid "Convert tag names for all images" msgstr "Convertir nombres de etiquetas para todas las imágenes" #: f.widgets.cc:229 msgid "Add/change/delete metadata for multiple images" msgstr "Añadir/cambiar/borrar metadatos en múltiples imágenes" #: f.widgets.cc:230 msgid "Edit image location and geotags" msgstr "Editar ubicación y geoetiquetas de la " #: f.widgets.cc:231 msgid "Add/revise geotags for multiple images" msgstr "Añadir/revisar geoetiquetas en múltiples imágenes" #: f.widgets.cc:232 msgid "Find all images for a location [date]" msgstr "Encontrar todas las imágenes para una localización (fecha)" #: f.widgets.cc:233 msgid "Find images meeting select criteria" msgstr "Encontrar imágenes que cumplan un criterio de selección" #: f.widgets.cc:236 msgid "Select object or area for editing" msgstr "Seleccionar objeto o área para editar" #: f.widgets.cc:237 msgid "Show (outline) existing area" msgstr "Mostrar un área existente (contorno)" #: f.widgets.cc:238 msgid "Hide existing area" msgstr "Ocultar áreas existentes" #: f.widgets.cc:239 msgid "Enable area for editing" msgstr "activar áre para editar" #: f.widgets.cc:240 msgid "Disable area for editing" msgstr "Desactivar área para editar" #: f.widgets.cc:241 msgid "Reverse existing area" msgstr "Invertir área existente" #: f.widgets.cc:242 msgid "Erase existing area" msgstr "Borrar área existente" #: f.widgets.cc:243 msgid "Copy area for later pasting into image" msgstr "Copiar área para posterior pegado en una imagen" #: f.widgets.cc:244 msgid "Paste previously copied area into image" msgstr "Pegar en la imagen àrea previamente copiada" #: f.widgets.cc:245 msgid "Open a file and paste as area into image" msgstr "Abrir un archivo y pegar como área en la imagen" #: f.widgets.cc:246 msgid "Save area to a file with transparency" msgstr "Guardar área como un archivo con transparencia" #: f.widgets.cc:249 msgid "Trim/Crop margins and/or Rotate" msgstr "Recortar/cortar márgenes y/o girar" #: f.widgets.cc:250 msgid "Upright a rotated image" msgstr "Enderezar una imagen girada" #: f.widgets.cc:251 f.widgets.cc:252 msgid "Fast auto enhance that may work OK" msgstr "Mejora automática rápida" #: f.widgets.cc:253 msgid "Adjust brightness, contrast, color" msgstr "Ajustar brillo, contraste, color" #: f.widgets.cc:254 msgid "Add local contrast, enhance details" msgstr "Añadir contraste local, mejorar detalles" #: f.widgets.cc:255 msgid "Adjust brightness distribution" msgstr "Ajustar distribución del brillo" #: f.widgets.cc:256 msgid "Flatten zonal brightness distribution" msgstr "Aplanar histograma zonal" #: f.widgets.cc:257 msgid "Change pixel dimensions" msgstr "Cambiar dimensiones en píxeles" #: f.widgets.cc:258 msgid "Mirror image horizontally or vertically" msgstr "Reflejar horizontal o verticalmente la imagen" #: f.widgets.cc:259 msgid "Write text on image" msgstr "Escribir un texto sobre la imagen" #: f.widgets.cc:260 msgid "Write lines or arrows on image" msgstr "Dibujar líneas o flechas en la imagen" #: f.widgets.cc:261 msgid "Fix brightness uniformity across image" msgstr "Corregir la uniformidad de brillo por zonas en la imagen" #: f.widgets.cc:262 msgid "Paint edit function gradually with mouse" msgstr "Función de edición pintando gradualmente con el ratón" #: f.widgets.cc:263 msgid "Leverage edits by brightness or color" msgstr "Edición por niveles de brillo o color" #: f.widgets.cc:264 msgid "Edit plugins menu or run a plugin function" msgstr "Editar men​​ú de plugins o ejecutar una función de plugin" #: f.widgets.cc:267 msgid "Make the image look sharper" msgstr "Hacer que la imagen se vea más enfocada" #: f.widgets.cc:268 msgid "Make the image look fuzzy" msgstr "Hacer que la imagen se vea desenfocada" #: f.widgets.cc:269 msgid "Filter noise from low-light photos" msgstr "Filtrar ruido de fotos hechas con poca luz" #: f.widgets.cc:270 msgid "Remove unwanted objects" msgstr "Eliminar objetos no deseados" #: f.widgets.cc:271 msgid "Fix red-eyes from electronic flash" msgstr "Corregir ojos rojos producidos por un flash electrónico" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Pintar usando el ratón" #: f.widgets.cc:273 msgid "Paint image transparency using the mouse" msgstr "Pintar transparencia de la imagen con el ratón" #: f.widgets.cc:274 msgid "Make BW/color, negative/positive, sepia" msgstr "Cambiar blanco y negro/color, negativo/positivo, sepia" #: f.widgets.cc:275 msgid "Shift/convert colors into other colors" msgstr "Cambiar/convertir colores en otros" #: f.widgets.cc:276 msgid "Adjust color using RGB or CMY colors" msgstr "Ajustar colores usando sistema RGB o CMY" #: f.widgets.cc:277 msgid "Adjust color using HSL colors" msgstr "Ajustar color usando sistema HSL" #: f.widgets.cc:278 msgid "Adjust color in selected image areas" msgstr "Ajustar color en áreas seleccionadas de la imagen" #: f.widgets.cc:279 msgid "Match colors on one image with another" msgstr "Concordar colores de una imagen con los de otra" #: f.widgets.cc:280 msgid "Convert to another color profile" msgstr "Convertir a otro perfil de color" #: f.widgets.cc:281 msgid "Remove dust spots from scanned slides" msgstr "Eliminar motas de polvo de diapositivas escaneadas" #: f.widgets.cc:282 msgid "Smoothen edges with jaggies" msgstr "Suavizar bordes con dientes de sierra" #: f.widgets.cc:283 msgid "Reduce Chromatic Abberation" msgstr "Reducir aberración cromática" #: f.widgets.cc:284 msgid "Erase known hot and dark pixels" msgstr "Eliminar píxeles calientes y negros conocidos" #: f.widgets.cc:287 msgid "Remove curvature, esp. panoramas" msgstr "Eliminar curvatura, esp. panoramas" #: f.widgets.cc:288 msgid "Straighten objects seen from an angle" msgstr "Enderezar objetos vistos desde un ángulo" #: f.widgets.cc:289 msgid "Distort image areas using the mouse" msgstr "Deformar áreas de la imagen usando el ratón" #: f.widgets.cc:290 f.widgets.cc:291 f.widgets.cc:292 msgid "Distort the whole image using the mouse" msgstr "Deformar toda la imagen usando el ratón" #: f.widgets.cc:293 msgid "Flatten a photographed book page" msgstr "Aplanar foto de una página impresa " #: f.widgets.cc:296 msgid "Reduce color depth (posterize)" msgstr "Reducir profundidad de color (posterizar)" #: f.widgets.cc:297 msgid "Convert to pencil sketch" msgstr "Convertir a un esbozo a lápiz" #: f.widgets.cc:298 msgid "Convert to line drawing (edge detection)" msgstr "Convertir a un dibujo de líneas (detección de bordes)" #: f.widgets.cc:299 msgid "Convert to solid color drawing" msgstr "Convertir a dibujo de colores sólidos" #: f.widgets.cc:300 msgid "Graduated Blur depending on contrast" msgstr "Desenfoque graduado dependiendo del contraste" #: f.widgets.cc:301 msgid "Create an embossed or 3D appearance" msgstr "Crear un relieve o apariencia 3D" #: f.widgets.cc:302 msgid "Convert to square tiles" msgstr "Convertir a baldosas cuadradas" #: f.widgets.cc:303 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "Convertir a puntos (efecto Roy Lichtenstein)" #: f.widgets.cc:304 msgid "Convert into a simulated painting" msgstr "Convertir a pintura simulada" #: f.widgets.cc:305 msgid "Change brightness or color radially" msgstr "Cambiar radialmente el brillo o el color" #: f.widgets.cc:306 msgid "Add texture to an image" msgstr "Añadir textura a una imagen" #: f.widgets.cc:307 msgid "Tile image with a repeating pattern" msgstr "Embaldosar imagen con un patrón repetitivo" #: f.widgets.cc:308 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:309 msgid "Process an image using a custom kernel" msgstr "Procesar una imagen usando una matriz personalizada" #: f.widgets.cc:310 msgid "Warp an image with a wave pattern" msgstr "Deformar una imagencon un patrón de ondas" #: f.widgets.cc:311 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:312 msgid "Make a spherical projection of an image" msgstr "Hacer una proyección esférica de una imagen" #: f.widgets.cc:315 msgid "Combine bright/dark images for better detail" msgstr "Combinar imágenes brillantes/oscuras para mejorar detalles" #: f.widgets.cc:316 msgid "Combine near/far focus images for deeper focus" msgstr "" "Combinar imágenes enfocadas cerca/lejos para mayor profundidad de campo" #: f.widgets.cc:317 msgid "Combine images to erase passing people, etc." msgstr "Combinar imágenes para borrar gente, coches, etc" #: f.widgets.cc:318 msgid "Combine noisy images into a low-noise image" msgstr "Combinaar imágenes ruidosas en una imagen de bajo ruido" #: f.widgets.cc:319 msgid "Combine images into a panorama" msgstr "Combinar imágenes en una panorámica" #: f.widgets.cc:320 msgid "Combine images into a vertical panorama" msgstr "Combinar imágenes en un panorama vertical" #: f.widgets.cc:321 msgid "Combine images into a panorama (panorama tools)" msgstr "Combinar imágenes en una panorámica (panorama tools)" #: f.widgets.cc:322 msgid "Arrange images and text in a layout (montage)" msgstr "Ordenar imágenes y texto en una capa (montaje)" #: f.widgets.cc:325 msgid "Index new files and make thumbnails" msgstr "Indexar nuevos archivos y crear miniaturas" #: f.widgets.cc:326 msgid "Change user preferences" msgstr "Cambiar preferencias del usuario" #: f.widgets.cc:327 msgid "Change Keyboard Shortcut Keys" msgstr "Canmbiar teclas de atajos de teclado" #: f.widgets.cc:328 msgid "Show a brightness distribution graph" msgstr "Mostrar histograma (distribución del brillo)" #: f.widgets.cc:329 msgid "Show or revise grid lines" msgstr "Mostrar o revisar líneas de rejilla" #: f.widgets.cc:330 msgid "Change color of foreground lines" msgstr "Cambiar color de líneas de contorno" #: f.widgets.cc:331 msgid "Show RGB colors at mouse click" msgstr "Mostrar colores RGB con un clic del ratón" #: f.widgets.cc:332 msgid "Magnify image around the mouse position" msgstr "Aumenta la imagen alrededor de la posición del ratón" #: f.widgets.cc:333 msgid "Highlight darkest and brightest pixels" msgstr "Resaltar píxeles más brillantes o más oscuros" #: f.widgets.cc:334 msgid "Chart to adjust monitor color" msgstr "Carta para ajustar el color del monitor" #: f.widgets.cc:335 msgid "Chart to adjust monitor gamma" msgstr "Carta para ajustar la gamma del monitor" #: f.widgets.cc:336 msgid "Change the GUI language" msgstr "cambiar el idioma de la interfaz de usuario" #: f.widgets.cc:337 msgid "Report missing translations" msgstr "Informe de traducciones que faltan" #: f.widgets.cc:338 msgid "Calibrate printer colors" msgstr "Calibrar colores de la impresora" #: f.widgets.cc:339 msgid "Memory and CPU (to terminal/logfile)" msgstr "Memoria y CPU (al terminal/archivo de registro)" #: f.widgets.cc:343 msgid "Rename/convert/resize/move multiple files" msgstr "Renombrar/convertir/redimensionar/mover múltiples archivos" #: f.widgets.cc:344 msgid "Upright multiple rotated image files" msgstr "Enderezar múltiples imagenes giradas" #: f.widgets.cc:345 msgid "Delete or Trash multiple files" msgstr "Borrar o eliminar múltiples imágenes" #: f.widgets.cc:346 msgid "Convert camera RAW files using DCraw" msgstr "Convertir archivos RAW usando DCraw" #: f.widgets.cc:347 msgid "Convert camera RAW files using Raw Therapee" msgstr "Convertir archivos RAW usando RawTherapee" #: f.widgets.cc:348 msgid "Build and run edit script files" msgstr "Escribir y ejecutar scripts editados" #: f.widgets.cc:349 msgid "Burn selected image files to CD or DVD" msgstr "Grabar imágenes seleccionadas en un CD o DVD" #: f.widgets.cc:350 msgid "Search all image files and report duplicates" msgstr "Buscar en todas las imágenes e informar de duplicados" #: f.widgets.cc:354 msgid "Quick Start mini-guide" msgstr "Mini guía de referencia rápida" #: f.widgets.cc:355 msgid "Read the user guide" msgstr "Leer la guía de usuario" #: f.widgets.cc:356 msgid "Recent user guide changes" msgstr "Cambios recientes en la guia de usuario" #: f.widgets.cc:357 msgid "Summary of image edit functions" msgstr "Resumen de las funciones de edición de imágenes" #: f.widgets.cc:358 msgid "Technical installation notes" msgstr "Notas técnicas de instalación" #: f.widgets.cc:359 msgid "List updates by Fotoxx version" msgstr "Listar actualizaciones por versión de Fotoxx" #: f.widgets.cc:360 msgid "View the log file and error messages" msgstr "Ver el archivo de registro y mensajes de error" #: f.widgets.cc:361 msgid "How to do Fotoxx translations" msgstr "Cómo hacer traducciones de Fotoxx" #: f.widgets.cc:362 msgid "Show the Fotoxx web page" msgstr "Muestra la página web de Fotoxx" #: f.widgets.cc:363 msgid "Version, license, contact, credits" msgstr "Versión, licencia, contacto, créditos" #: f.widgets.cc:366 msgid "Organize images into albums" msgstr "Organizar imágenes en álbumes" #: f.widgets.cc:367 msgid "Start a slide show" msgstr "Iniciar un diaporama" #: f.widgets.cc:368 msgid "Set desktop wallpaper from current Fotoxx image" msgstr "Establecer fondo de escritorio desde la imagen de Fotoxx actual" #: f.widgets.cc:369 msgid "Cycle desktop wallpaper from a Fotoxx album" msgstr "Fondo de escritorio cíclico desde un álbum de Fotoxx" #: f.widgets.cc:391 msgid "New Window" msgstr "Nueva ventana" #: f.widgets.cc:392 msgid "Sync Gallery" msgstr "Sincronizar galería" #: f.widgets.cc:393 msgid "Recently Seen Images" msgstr "Imágenes vistas recientemente " #: f.widgets.cc:394 msgid "Newest Images" msgstr "Imágenes más recientes" #: f.widgets.cc:396 msgid "Open Previous File" msgstr "Abrir archivo anterior" #: f.widgets.cc:397 msgid "Open RAW (ufraw)" msgstr "Abrir RAW (ufraw)" #: f.widgets.cc:398 msgid "Open RAW (Raw Therapee)" msgstr "Abrir RAW (Raw Therapee)" #: f.widgets.cc:399 msgid "New Blank Image" msgstr "Nueva imagen en blanco" #: f.widgets.cc:402 msgid "Delete Image File" msgstr "Borrar imagen" #: f.widgets.cc:403 msgid "Print Image" msgstr "Imprimir imagen" #: f.widgets.cc:404 msgid "Print Calibrated Image" msgstr "Imprimir imagen calibrada" #: f.widgets.cc:405 f.widgets.cc:550 msgid "Set Desktop Wallpaper" msgstr "Establecer fondo de escritorio" #: f.widgets.cc:409 msgid "View Metadata (short)" msgstr "Ver metadatos (corto)" #: f.widgets.cc:410 msgid "View Metadata (long)" msgstr "Ver metadatos (largo)" #: f.widgets.cc:411 msgid "Show Captions on Image" msgstr "Mostrar leyendas en la imagen" #: f.widgets.cc:413 msgid "Edit Any Metadata" msgstr "Editar cualquier metadato" #: f.widgets.cc:416 f.widgets.cc:541 msgid "Batch Rename Tags" msgstr "Renombrar etiquetas en lote" #: f.widgets.cc:417 f.widgets.cc:542 msgid "Batch Add/Change Metadata" msgstr "Añadir/cambiar metadatos en lote" #: f.widgets.cc:420 f.widgets.cc:544 f.widgets.cc:618 msgid "Images by Geotag" msgstr "Imágenes por Geoetiquetas" #: f.widgets.cc:421 f.widgets.cc:545 f.widgets.cc:619 msgid "Search Images" msgstr "Buscar imágenes" #: f.widgets.cc:424 fotoxx.h:1168 msgid "Select" msgstr "Seleccionar" #: f.widgets.cc:425 fotoxx.h:1170 msgid "Show" msgstr "Mostrar" #: f.widgets.cc:426 fotoxx.h:1124 msgid "Hide" msgstr "Ocultar" #: f.widgets.cc:427 fotoxx.h:1110 msgid "Enable" msgstr "Activar" #: f.widgets.cc:428 fotoxx.h:1106 msgid "Disable" msgstr "Desactivar" #: f.widgets.cc:429 fotoxx.h:1127 msgid "Invert" msgstr "Invertir" #: f.widgets.cc:430 fotoxx.h:1184 msgid "Unselect" msgstr "Deseleccionar" #: f.widgets.cc:431 msgid "Copy Area" msgstr "Copiar área" #: f.widgets.cc:432 msgid "Paste Area" msgstr "Pegar área" #: f.widgets.cc:433 msgid "Open Area File" msgstr "Abrir archivo de área" #: f.widgets.cc:434 msgid "Save Area File" msgstr "Guardar archivo de área" #: f.widgets.cc:439 msgid "Voodoo 1" msgstr "Automejora 1" #: f.widgets.cc:440 msgid "Voodoo 2" msgstr "Automejora 2" #: f.widgets.cc:442 f.widgets.cc:687 msgid "Brightness Dist." msgstr "Histograma" #: f.widgets.cc:443 f.widgets.cc:688 msgid "Zonal Flatten" msgstr "Aplanado zonal" #: f.widgets.cc:447 msgid "Add Text" msgstr "Añadir texto" #: f.widgets.cc:448 msgid "Add Lines" msgstr "Añadir líneas" #: f.widgets.cc:451 msgid "Plugins" msgstr "Plugins" #: f.widgets.cc:456 msgid "Denoise" msgstr "Reducir ruido" #: f.widgets.cc:458 msgid "Red Eyes" msgstr "Ojos rojos" #: f.widgets.cc:463 msgid "Adjust RGB/CMY" msgstr "Ajustar RGB/CMY" #: f.widgets.cc:464 msgid "Adjust HSL" msgstr "Ajystar HSL" #: f.widgets.cc:465 msgid "Brightness Ramp" msgstr "Rampa de brillo" #: f.widgets.cc:467 msgid "Match Colors" msgstr "Concordar colores" #: f.widgets.cc:468 msgid "Color Profile" msgstr "perfil de color" #: f.widgets.cc:470 msgid "Anti-Alias" msgstr "Suavizar bordes" #: f.widgets.cc:476 msgid "Fix Perspective" msgstr "Corregir perspectiva" #: f.widgets.cc:481 msgid "Flatten Book Page" msgstr "Aplanar página impresa" #: f.widgets.cc:484 msgid "Color Depth" msgstr "Profundidad de color" #: f.widgets.cc:485 msgid "Sketch" msgstr "Esbozo" #: f.widgets.cc:489 msgid "Embossing" msgstr "Relieve" #: f.widgets.cc:491 msgid "Dots" msgstr "Puntos" #: f.widgets.cc:492 msgid "Painting" msgstr "Pintura" #: f.widgets.cc:494 msgid "Texture" msgstr "Textura" #: f.widgets.cc:496 msgid "Mosaic" msgstr "Mosaico" #: f.widgets.cc:503 msgid "High Dynamic Range" msgstr "HDR alto rango dinámico" #: f.widgets.cc:504 msgid "High Depth of Field" msgstr "HDF alta profundidad de campo" #: f.widgets.cc:505 msgid "Stack/Paint" msgstr "Apilar/pintar" #: f.widgets.cc:506 msgid "Stack/Noise" msgstr "Apilar/ruido" #: f.widgets.cc:507 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:508 msgid "Vertical Panorama" msgstr "Panorama vertical " #: f.widgets.cc:509 msgid "PT Panorama" msgstr "PT panorama" #: f.widgets.cc:510 msgid "Mashup" msgstr "Montaje" #: f.widgets.cc:515 msgid "Keyboard Shortcuts" msgstr "Atajos de teclado" #: f.widgets.cc:516 msgid "Show Brightness Dist." msgstr "Mostrar histograma" #: f.widgets.cc:521 msgid "Dark/Bright Pixels" msgstr "Sobre/subexposición" #: f.widgets.cc:522 msgid "Monitor Color" msgstr "Color del monitor" #: f.widgets.cc:524 msgid "Change Language" msgstr "Cambiar idioma" #: f.widgets.cc:525 msgid "Missing Translations" msgstr "Traducciones que faltan" #: f.widgets.cc:527 msgid "Resources" msgstr "Recursos" #: f.widgets.cc:534 msgid "Batch RAW (DCraw)" msgstr "Convertir Raw en lote (DCraw)" #: f.widgets.cc:535 msgid "Batch RAW (Raw Therapee)" msgstr "Convertir RAW en lote (RawTherapee)" #: f.widgets.cc:536 msgid "Script Files" msgstr "Scripts" #: f.widgets.cc:537 msgid "Burn Images to CD/DVD" msgstr "Grabar imágenes en un CD/DVD" #: f.widgets.cc:551 msgid "Cycle Desktop Wallpaper" msgstr "Fondo de escritorio cíclico" #: f.widgets.cc:568 f.widgets.cc:591 f.widgets.cc:613 msgid "Gallery" msgstr "Galeria" #: f.widgets.cc:569 f.widgets.cc:592 f.widgets.cc:614 msgid "World Maps" msgstr "Mapas mundiales" #: f.widgets.cc:571 msgid "Favorites" msgstr "Favoritos" #: f.widgets.cc:573 fotoxx.h:1164 msgid "Save" msgstr "Guardar" #: f.widgets.cc:574 msgid "Prev/Next" msgstr "Ant/Sig" #: f.widgets.cc:575 msgid "Metadata" msgstr "Metadatos" #: f.widgets.cc:576 msgid "Areas" msgstr "Areas" #: f.widgets.cc:577 fotoxx.h:1109 msgid "Edit" msgstr "Editar" #: f.widgets.cc:578 msgid "Repair" msgstr "Reparar" #: f.widgets.cc:579 msgid "Bend" msgstr "Deformar" #: f.widgets.cc:580 msgid "Effects" msgstr "Efectos" #: f.widgets.cc:581 msgid "Combine" msgstr "Combinar" #: f.widgets.cc:582 msgid "Undo/Redo" msgstr "Desh/Reh" #: f.widgets.cc:583 msgid "Tools" msgstr "Herramientas" #: f.widgets.cc:594 msgid "Sync.G" msgstr "Sincr.G" #: f.widgets.cc:595 msgid "Albums" msgstr "Álbums" #: f.widgets.cc:596 msgid "Bookmarks" msgstr "Marcas" #: f.widgets.cc:605 msgid "Batch" msgstr "Lote" #: f.widgets.cc:616 msgid "Choose Map" msgstr "Elegir mapa" #: f.widgets.cc:617 msgid "Search Range" msgstr "Rango de búsqueda" #: f.widgets.cc:625 fotoxx.h:1182 msgid "Undo" msgstr "Deshacer" #: f.widgets.cc:626 fotoxx.h:1155 msgid "Redo" msgstr "Rehacer" #: f.widgets.cc:667 msgid "Popup Image" msgstr "Imagen emergente" #: f.widgets.cc:668 msgid "Popup Image (add)" msgstr "Imagen emergente (añadir)" #: f.widgets.cc:672 fotoxx.h:1158 msgid "Rename" msgstr "Renombrar" #: f.widgets.cc:673 msgid "Copy to Location" msgstr "Cambiar a la ubicación" #: f.widgets.cc:674 msgid "Move to Location" msgstr "Mover a la ubicación" #: f.widgets.cc:675 msgid "Copy to Clipboard" msgstr "Copiar al portapapeles" #: f.widgets.cc:676 msgid "Remove from Album" msgstr "Eliminar del álbum" #: f.widgets.cc:677 msgid "Cut to Image Cache" msgstr " Cortar al caché de imagen" #: f.widgets.cc:678 msgid "Copy to Image Cache" msgstr "Copiar al caché de imagen " #: f.widgets.cc:679 msgid "Paste Image Cache Here (clear)" msgstr "Pegar caché de imagen aquí (borrar)" #: f.widgets.cc:680 msgid "Paste Image Cache Here (keep)" msgstr "Pegar caché de imagen aquí (guardar)" #: f.widgets.cc:681 msgid "Paste Current Image File Here" msgstr "Pegar aqui imagen actual" #: f.widgets.cc:685 msgid "Voodoo Enhance" msgstr "Mejora automática" #: f.widgets.cc:690 msgid "Select Area" msgstr "Seleccionar área" #: f.widgets.cc:693 fotoxx.h:1178 msgid "Trash" msgstr "Papelera" #: f.widgets.cc:819 msgid "Image Locations" msgstr "Ubicación de imágenes" #: fotoxx-15.11.cc:373 msgid "Please install missing programs:" msgstr "Por favor instalar los programas necesarios:" #: fotoxx-15.11.cc:662 msgid "Kill active dialog?" msgstr "¿Cerrar diálogo activo?" #: fotoxx-15.11.cc:733 msgid "(reduced)" msgstr "(reducido)" #: fotoxx-15.11.cc:734 msgid "area active" msgstr "área activa" #: fotoxx-15.11.cc:735 msgid "dialog open" msgstr "diálogo abierto" #: fotoxx-15.11.cc:736 msgid "blocked" msgstr "bloqueado" #: fotoxx-15.11.cc:786 msgid "edits" msgstr "ediciones" #: fotoxx-15.11.cc:2793 msgid "Exceed 50 anchor points" msgstr "Excedidos 50 puntos de anclaje" #: fotoxx-15.11.cc:3013 msgid "load curve from a file" msgstr "Cargar curva desde un archivo" #: fotoxx-15.11.cc:3079 msgid "curve file is invalid" msgstr "Archivo de curva no válido" #: fotoxx-15.11.cc:3093 msgid "save curve to a file" msgstr "Guardar la curva como un archivo" #: fotoxx-15.11.cc:3165 msgid "Too many edits, please save image" msgstr "Demasiadas ediciones, por favcor guarde la imagen" #: fotoxx-15.11.cc:3170 msgid "this function cannot be scripted" msgstr "" #: fotoxx-15.11.cc:3187 msgid "" "Select area not active.\n" "Continue?" msgstr "" "La selección de área no está activada.\n" "¿Continuar?" #: fotoxx-15.11.cc:3499 msgid "file data does not fit dialog" msgstr "los datos del archivo no se ajustan al diálogo" #: fotoxx-15.11.cc:3509 msgid "Save settings to a file" msgstr "Guardar ajustes en un archivo" #: fotoxx-15.11.cc:3825 msgid "This action will discard changes to current image" msgstr "Esta acción descartará los cambios a la imagen actual" #: fotoxx-15.11.cc:3826 msgid "prior function still active" msgstr "función anterior todavía activa" #: fotoxx-15.11.cc:3827 fotoxx.h:1128 msgid "Keep" msgstr "Guardar" #: fotoxx-15.11.cc:3828 msgid "Discard" msgstr "Descartar" #: fotoxx.h:1080 msgid "Add" msgstr "Añadir" #: fotoxx.h:1081 msgid "Add All" msgstr "Añadir todo" #: fotoxx.h:1083 msgid "Amount" msgstr "Cantidad" #: fotoxx.h:1084 msgid "Angle" msgstr "Ángulo" #: fotoxx.h:1085 msgid "Apply" msgstr "Aplicar" #: fotoxx.h:1086 msgid "Auto" msgstr "Auto" #: fotoxx.h:1087 msgid "Black" msgstr "Negro" #: fotoxx.h:1088 msgid "Blend Width" msgstr "Ancho de mezclado" #: fotoxx.h:1090 zfuncs.cc:10098 msgid "bottom" msgstr "abajo" #: fotoxx.h:1092 msgid "Browse" msgstr "Examinar" #: fotoxx.h:1093 msgid "Cancel" msgstr "Cancelar" #: fotoxx.h:1094 msgid "Center" msgstr "centro" #: fotoxx.h:1095 msgid "Choose" msgstr "Elegir" #: fotoxx.h:1096 msgid "Clear" msgstr "Limpiar" #: fotoxx.h:1097 msgid "Color" msgstr "Color" #: fotoxx.h:1098 msgid "continue" msgstr "continuar" #: fotoxx.h:1100 msgid "Copy" msgstr "Copiar" #: fotoxx.h:1101 msgid "Create" msgstr "Crear" #: fotoxx.h:1102 msgid "Curve File:" msgstr "Archivo de curva:" #: fotoxx.h:1103 msgid "Cut" msgstr "Cortar" #: fotoxx.h:1104 msgid "Deband" msgstr "Eliminar bandas" #: fotoxx.h:1105 msgid "Delete" msgstr "Borrar" #: fotoxx.h:1107 msgid "Done" msgstr "Hecho" #: fotoxx.h:1108 msgid "edge" msgstr "borde" #: fotoxx.h:1111 msgid "Erase" msgstr "Borrar" #: fotoxx.h:1112 msgid "Fetch" msgstr "Extraer" #: fotoxx.h:1113 msgid "output file already exists" msgstr "el archivo de salida ya existe" #: fotoxx.h:1114 #, c-format msgid "%d files selected" msgstr "%d archivos seleccionados" #: fotoxx.h:1115 msgid "Find" msgstr "Encontrar" #: fotoxx.h:1116 msgid "Finish" msgstr "Terminar" #: fotoxx.h:1117 msgid "Flatten" msgstr "Aplanar" #: fotoxx.h:1118 msgid "Font" msgstr "Tipo de letra" #: fotoxx.h:1119 msgid "Geotags" msgstr "Geoetiquetas" #: fotoxx.h:1121 msgid "Grid" msgstr "Rejilla" #: fotoxx.h:1122 msgid "Height" msgstr "Alto" #: fotoxx.h:1125 msgid "Images" msgstr " Imágenes" #: fotoxx.h:1126 msgid "Insert" msgstr "Insertar" #: fotoxx.h:1129 zfuncs.cc:10102 msgid "left" msgstr "izquierda" #: fotoxx.h:1130 msgid "limit" msgstr "límite" #: fotoxx.h:1131 msgid "Load" msgstr "Cargar" #: fotoxx.h:1132 msgid "Make" msgstr "Hacer" #: fotoxx.h:1134 msgid "Map" msgstr "Mapa" #: fotoxx.h:1135 msgid "Max" msgstr "Máx" #: fotoxx.h:1136 msgid "Negative" msgstr "Negativo" #: fotoxx.h:1137 msgid "New" msgstr "Nuevo" #: fotoxx.h:1138 msgid "Next" msgstr "Siguiente" #: fotoxx.h:1139 zfuncs.cc:9190 msgid "No" msgstr "No" #: fotoxx.h:1140 msgid "no images" msgstr "sin imágenes" #: fotoxx.h:1142 msgid "no selection" msgstr "sin selección" #: fotoxx.h:1143 msgid "OK" msgstr "OK" #: fotoxx.h:1145 msgid "Paste" msgstr "Pegar" #: fotoxx.h:1146 msgid "Pause" msgstr "Pausa" #: fotoxx.h:1147 msgid "Percent" msgstr "Porcentaje" #: fotoxx.h:1149 msgid "Presets" msgstr "Predefinidos" #: fotoxx.h:1150 msgid "Prev" msgstr "Anterior" #: fotoxx.h:1151 msgid "Proceed" msgstr "Proceder" #: fotoxx.h:1153 msgid "range" msgstr "rango" #: fotoxx.h:1156 msgid "Reduce" msgstr "Reducir" #: fotoxx.h:1157 msgid "Remove" msgstr "Eliminar" #: fotoxx.h:1159 msgid "Replace" msgstr "" #: fotoxx.h:1160 msgid "Reserved" msgstr "Reservado" #: fotoxx.h:1161 msgid "Reset" msgstr "Restablecer" #: fotoxx.h:1162 zfuncs.cc:10106 msgid "right" msgstr "derecha" #: fotoxx.h:1163 msgid "Rotate" msgstr "Rotar" #: fotoxx.h:1165 msgid "Unknown file type, save as tiff/jpeg/png to edit" msgstr "Tipo de archivo desconocido, guarde como tiff/jpeg/png para editarlo" #: fotoxx.h:1166 msgid "Search" msgstr "Buscar" #: fotoxx.h:1167 msgid "Seconds" msgstr "Segundos" #: fotoxx.h:1171 msgid "Size" msgstr "Tamaño" #: fotoxx.h:1172 msgid "Start" msgstr "Comenzar" #: fotoxx.h:1173 msgid "Strength" msgstr "Fuerza" #: fotoxx.h:1174 msgid "Threshold" msgstr "Umbral" #: fotoxx.h:1175 #, c-format msgid "exceed %d files" msgstr "se excedió en %d archivos" #: fotoxx.h:1176 zfuncs.cc:10094 msgid "top" msgstr "arriba" #: fotoxx.h:1177 msgid "Transparency" msgstr "Transparencia" #: fotoxx.h:1179 msgid "Trim" msgstr "Recortar" #: fotoxx.h:1180 msgid "Undo All" msgstr "Deshacer todo" #: fotoxx.h:1181 msgid "Undo Last" msgstr "Deshacer el último" #: fotoxx.h:1183 msgid "Unfinish" msgstr "Sin cerrar" #: fotoxx.h:1185 msgid "View" msgstr "Ver" #: fotoxx.h:1186 msgid "Web" msgstr "Web" #: fotoxx.h:1187 msgid "White" msgstr "Blanco" #: fotoxx.h:1188 msgid "Width" msgstr "Ancho" #: fotoxx.h:1189 msgid "x-offset" msgstr "desplazamiento X" #: fotoxx.h:1190 msgid "y-offset" msgstr "desplazamiento Y" #: fotoxx.h:1191 zfuncs.cc:9190 msgid "Yes" msgstr "Sí" #: zfuncs.cc:1601 #, c-format msgid "" "create directory? \n" " %s" msgstr "" "¿Crear directorio? \n" " %s" #: zfuncs.cc:4378 msgid "user guide not found" msgstr "guia de usuario no encontrada" #: zfuncs.cc:5196 #, c-format msgid "cannot open file %s" msgstr "No puedo abrir el archivo %s" #: zfuncs.cc:5232 msgid "save screen to file" msgstr "guardar pantalla a archivo" #: zfuncs.cc:9266 zfuncs.cc:9761 zfuncs.cc:10081 msgid "cancel" msgstr "cancelar" #: zfuncs.cc:9722 msgid "choose file" msgstr "elegir archivo" #: zfuncs.cc:9727 msgid "choose files" msgstr "elegir archivos" #: zfuncs.cc:9732 msgid "save" msgstr "guardar" #: zfuncs.cc:9738 msgid "choose folder" msgstr "elegir carpeta" #: zfuncs.cc:9743 msgid "choose folders" msgstr "elegir carpetas" #: zfuncs.cc:9748 msgid "create folder" msgstr "crear carpeta" #: zfuncs.cc:9755 msgid "hidden" msgstr "ocultar" #: zfuncs.cc:10081 msgid "done" msgstr "hecho" #: zfuncs.cc:10081 zfuncs.cc:10090 msgid "margins" msgstr "márgenes" #: zfuncs.cc:10111 msgid "image scale" msgstr "escalar imagen" #: zfuncs.cc:10113 msgid "percent" msgstr "porcentual" #: zfuncs.cc:10120 msgid "image" msgstr "imagen" #: zfuncs.cc:10124 msgid "width" msgstr "ancho" #: zfuncs.cc:10128 msgid "height" msgstr "alto" #~ msgid "" #~ "new name/base/adder unreasonable\n" #~ " e.g. newname ### 100 10" #~ msgstr "" #~ "nuevo nombre/inicio/incremento no razonable\n" #~ "p.e. nuevonombre ### 100 10" #~ msgid "Median Brightness" #~ msgstr "Mediana de brillo" #~ msgid "Flatten Outliers 2" #~ msgstr "aplanar bordes 2" #~ msgid "Flatten Outliers 1" #~ msgstr "Aplanar bordes 1" #~ msgid "Kuwahara method" #~ msgstr "Método Kuwahara" #~ msgid "brightness gradient" #~ msgstr "gradiente de brillo" #~ msgid "unsharp mask" #~ msgstr "máscara de desenfoque" #~ msgid "Lock aspect ratio" #~ msgstr "Bloquear la relación de aspecto" #~ msgid "Adjust Brightness Dist." #~ msgstr "Ajustar histograma" #~ msgid "Cycle Desktop Image" #~ msgstr "Fondo de escritorio cíclico" #~ msgid "Set Desktop Image" #~ msgstr "Establecer fondo de escritorio" #~ msgid "Cycle desktop images from a Fotoxx album" #~ msgstr "Fondos de escritorio cíclicos desde un álbum de Fotoxx" #~ msgid "Set desktop image from current Fotoxx image" #~ msgstr "Establecer fondo de escritorio con la imagen actual" #~ msgid "Albums: Manage Albums, Slide Show, Desktop Background" #~ msgstr "Albumes: Administrar álbumes, Diaporama, Fondo de escritorio" #~ msgid "jpeg quality must be 1-100" #~ msgstr "la calidad de JPEG debe ser 1-100" #~ msgid "(%d images)" #~ msgstr "(%d imágenes)" #~ msgid "(0 images)" #~ msgstr "(0 imágenes)" fotoxx-15.11.1/locales/translate-ca.po0000664000175000017500000032462612616075370016235 0ustar micomico# Traducció al català del paquet Fotoxx # Copyright (C) 2011 THE home'S COPYRIGHT HOLDER # Aquest fitxer es distribueix Igual que el paquet original. # Josep Antoni Miralles Puignau , 2012.2013,2014,2015 # msgid "" msgstr "" "Project-Id-Version: home 2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-11-01 13:54+0100\n" "PO-Revision-Date: 2013-12-30 12:36+0100\n" "Last-Translator: Josep Antoni Miralles Puignau \n" "Language-Team: Catalán, Valenciano <>\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:78 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:80 msgid "Drag album thumbnail to new position." msgstr "Arrossegar miniatura de l'àlbum a la nova posició" #: f.albums.cc:109 f.widgets.cc:548 msgid "Manage Albums" msgstr "Gestionar àlbums" #: f.albums.cc:120 f.albums.cc:258 msgid "Create or replace an album" msgstr "Crear o substituir un àlbum" #: f.albums.cc:122 msgid "Album to view or edit" msgstr "Àlbum per veure o editar" #: f.albums.cc:124 msgid "Select images, add to cache" msgstr "Seleccionar imatges, afegir a la cache" #: f.albums.cc:126 msgid "Clear image cache" msgstr "Netejar cache d'imatge" #: f.albums.cc:128 f.albums.cc:150 msgid "Delete an album" msgstr "Esborrar un àlbum" #: f.albums.cc:149 msgid "Choose Album" msgstr "Escollir un àlbum" #: f.albums.cc:151 #, c-format msgid "Image cache has %d images" msgstr "" #: f.albums.cc:152 msgid "cache added to empty album" msgstr "caché afegida a un àlbum buit" #: f.albums.cc:207 #, c-format msgid "delete %s ?" msgstr "esborrar %s ?" #: f.albums.cc:242 #, c-format msgid "fill from image cache (%d images)" msgstr "emplenar des del cachè (%d imatges)" #: f.albums.cc:260 f.albums.cc:297 msgid "Album Name" msgstr "Nom de l'àlbum" #: f.albums.cc:266 msgid "make an initially empty album" msgstr "crear un àlbun buit" #: f.albums.cc:268 msgid "fill from current gallery" msgstr "emplenar des de la galeria actual" #: f.albums.cc:311 msgid "enter an album name" msgstr "entrar un nom d'àlbum" #: f.albums.cc:338 msgid "gallery is empty" msgstr "la galeria està buida" #: f.albums.cc:365 msgid "new album created" msgstr "creat nou àlbum" #: f.albums.cc:1099 msgid "Press ESC to exit slide show" msgstr "Prémer ESC per sortir del diaporama" #: f.albums.cc:1101 msgid "instant" msgstr "instantània" #: f.albums.cc:1102 msgid "fade-in" msgstr "fusió endins" #: f.albums.cc:1103 msgid "roll-right" msgstr "enrotllar a la dreta" #: f.albums.cc:1104 msgid "roll-down" msgstr "enrotllar abaix" #: f.albums.cc:1105 msgid "venetian" msgstr "persiana" #: f.albums.cc:1106 msgid "grate" msgstr "enreixat" #: f.albums.cc:1107 msgid "rectangle" msgstr "rectangle" #: f.albums.cc:1108 msgid "implode" msgstr "implosionar" #: f.albums.cc:1109 msgid "explode" msgstr "explotar" #: f.albums.cc:1110 msgid "radar" msgstr "radar" #: f.albums.cc:1111 msgid "Japanese-fan" msgstr "ventall japonés" #: f.albums.cc:1112 msgid "jaws" msgstr "dents" #: f.albums.cc:1113 msgid "ellipse" msgstr "elipsi" #: f.albums.cc:1114 msgid "raindrops" msgstr "gotes de pluja" #: f.albums.cc:1115 msgid "doubledoor" msgstr "doble porta" #: f.albums.cc:1116 msgid "rotate" msgstr "girar" #: f.albums.cc:1117 msgid "fallover" msgstr "cap abaix" #: f.albums.cc:1118 msgid "sphereoid" msgstr "" #: f.albums.cc:1139 f.widgets.cc:549 msgid "Slide Show" msgstr "Diaporama" #: f.albums.cc:1150 msgid "Clip Limit" msgstr "Límit de sequencia" #: f.albums.cc:1154 msgid "Music File" msgstr "Arxiu de Música" #: f.albums.cc:1159 msgid "Full Screen" msgstr "A tota pantalla" #: f.albums.cc:1160 msgid "Auto-replay" msgstr "Auto reiniciar" #: f.albums.cc:1166 msgid "Customize:" msgstr "Personalitzar" #: f.albums.cc:1167 msgid "transitions" msgstr "transicions" #: f.albums.cc:1168 msgid "image files" msgstr "archivos de imagen" #: f.albums.cc:1173 f.albums.cc:1289 #, c-format msgid "%d images" msgstr "%d imatges" #: f.albums.cc:1195 f.albums.cc:1259 f.albums.cc:1349 f.albums.cc:1462 #: f.albums.cc:1655 f.albums.cc:1739 f.albums.cc:1756 f.albums.cc:1977 msgid "invalid album" msgstr "àlbum invàlid" #: f.albums.cc:1273 msgid "open album" msgstr "obrir àlbum" #: f.albums.cc:1305 msgid "Select music file" msgstr "Seleccioneu arxiu de música" #: f.albums.cc:1329 msgid "select random (if 5+ enabled)" msgstr "Selecció aleatòria (si +5 activat)" #: f.albums.cc:1353 msgid "Transition Preferences" msgstr "Preferències de transició" #: f.albums.cc:1362 msgid "transition" msgstr "transició" #: f.albums.cc:1363 msgid "enabled" msgstr "activada" #: f.albums.cc:1364 msgid "slowdown" msgstr "ralentitzar" #: f.albums.cc:1365 msgid "preference" msgstr "preferència" #: f.albums.cc:1468 msgid "Image Preferences" msgstr "Preferències d'imatge" #: f.albums.cc:1472 f.file.cc:1159 f.file.cc:1400 f.file.cc:1608 f.meta.cc:437 msgid "Image File:" msgstr "Imatge:" #: f.albums.cc:1476 msgid "Play tone when image shows" msgstr "Reproduïr música mentre es veu la imatge" #: f.albums.cc:1479 msgid "Show image caption" msgstr "Mostrar títul del diaporama" #: f.albums.cc:1484 msgid "Show image comments" msgstr "Mostrar comentaris de la imatge" #: f.albums.cc:1489 msgid "Wait before zoom" msgstr "Espera abans del zoom" #: f.albums.cc:1494 msgid "Zoom type:" msgstr "Tipus de zoom" #: f.albums.cc:1495 msgid "none" msgstr "cap" #: f.albums.cc:1496 msgid "zoom-in" msgstr "aproximar" #: f.albums.cc:1497 msgid "zoom-out" msgstr "allunyar" #: f.albums.cc:1500 msgid "Zoom size (x)" msgstr "Tamany de zoon (x)" #: f.albums.cc:1503 msgid "Steps" msgstr "Pasos" #: f.albums.cc:1507 msgid "Zoom image location:" msgstr "Localitzaciò del zoom d'imatge" #: f.albums.cc:1515 msgid "Wait after zoom" msgstr "Espera desprès del zoom" #: f.albums.cc:1520 msgid "Transition to next image" msgstr "Transiciò a la pròxima imatge" #: f.albums.cc:1523 f.albums.cc:1625 msgid "next" msgstr "següent" #: f.albums.cc:1962 f.albums.cc:2120 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "error de format d'arxiu: \n" " %s" #: f.area.cc:79 msgid "Select Area for Edits" msgstr "Seleccionar àrea per editar" #: f.area.cc:80 f.edit.cc:5927 msgid "Press F1 for help" msgstr "Prèmer F1 per ajuda" #: f.area.cc:89 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Seleccionar àrea no suportat \n" "Per aquesta funció d'edició" #: f.area.cc:119 msgid "select rectangle" msgstr "seleccionar rectangle" #: f.area.cc:120 msgid "select ellipse" msgstr "seleccionar ellipse" #: f.area.cc:123 msgid "freehand draw" msgstr "dibuixar a mà alçada" #: f.area.cc:124 msgid "follow edge" msgstr "seguir contorn" #: f.area.cc:125 msgid "draw/replace" msgstr "dibuixar/substituïr" #: f.area.cc:128 msgid "select area within mouse" msgstr "Seleccionar àrea amb el ratolí" #: f.area.cc:131 msgid "select one matching color within mouse" msgstr "Seleccioneu un color coincident amb el ratolí" #: f.area.cc:135 msgid "select all matching colors within mouse" msgstr "seleccionar amb el ratolí tots els colors coincidents" #: f.area.cc:139 f.edit.cc:5972 msgid "mouse radius" msgstr "radi del ratolí" #: f.area.cc:142 msgid "match level %" msgstr "nivell de coincidència %" #: f.area.cc:147 msgid "search range" msgstr "rang de cerca" #: f.area.cc:149 msgid "firewall" msgstr "talla-focs" #: f.area.cc:152 msgid "Area Edge Blend Width" msgstr "Ample de barreja dels línits de l'àrea" #: f.area.cc:155 msgid "Edge Creep" msgstr "Límit progressiu" #: f.area.cc:160 msgid "Line Color:" msgstr "Color de línia" #: f.area.cc:180 f.area.cc:181 msgid "area edits fade away within edge distance" msgstr "les edicions d'àrea s'esvaeixen a les vores" #: f.area.cc:360 f.area.cc:543 #, c-format msgid "exceed %d edits" msgstr "excedit %d edicions" #: f.area.cc:1364 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:1392 msgid "finish area" msgstr "acabar àrea" #: f.area.cc:1400 msgid "extend to corner:" msgstr "extendre fins a la cantonada" #: f.area.cc:1441 msgid "searching" msgstr "buscant" #: f.area.cc:1472 msgid "outline has a gap" msgstr "el contorn te un forat" #: f.area.cc:1476 msgid "success" msgstr "èxit" #: f.area.cc:1613 f.area.cc:1640 #, c-format msgid "found %d pixels" msgstr "trobats %d pixels" #: f.area.cc:1935 f.area.cc:1952 f.area.cc:1972 f.area.cc:2287 f.area.cc:2363 #: f.area.cc:2520 msgid "the area is not finished" msgstr "l'àrea no està acabada" #: f.area.cc:2061 msgid "Edge calculation in progress" msgstr "Càlcul del límit en progrés" #: f.area.cc:2070 msgid "Area Edge Calc" msgstr "Càlcul del límit de l'àrea" #: f.area.cc:2471 msgid "load area from a file" msgstr "Carregar àrea des d'una imatge" #: f.area.cc:2554 msgid "save area as a PNG file" msgstr "guardar àrea com un arxiu PNG" #: f.area.cc:2582 msgid "position with mouse click/drag" msgstr "posició amb el clic del ratolí/arrossegament" #: f.area.cc:2638 msgid "Paste Image" msgstr "Enganxar imatge" #: f.area.cc:2643 f.batch.cc:1169 f.batch.cc:1660 msgid "resize" msgstr "redimensionar" #: f.batch.cc:107 f.widgets.cc:531 msgid "Batch Convert" msgstr "Convertir en lot" #: f.batch.cc:114 f.file.cc:942 msgid "New Name" msgstr "nou nom" #: f.batch.cc:118 msgid "Sequence Numbers" msgstr "" #: f.batch.cc:120 msgid "base" msgstr "inici" #: f.batch.cc:122 msgid "adder" msgstr "increment" #: f.batch.cc:127 msgid "New Location" msgstr "Nova ubicació" #: f.batch.cc:132 msgid "New File Type" msgstr "Nou tipus d'arxiu" #: f.batch.cc:136 f.batch.cc:144 f.repair.cc:3861 msgid "no change" msgstr "sense canvis" #: f.batch.cc:139 msgid "max. Width" msgstr "Ample màx." #: f.batch.cc:147 f.batch.cc:565 msgid "Delete Originals" msgstr "Esborrar originals" #: f.batch.cc:148 f.batch.cc:562 msgid "Copy Metadata" msgstr "Copiar metadades" #: f.batch.cc:149 f.batch.cc:563 f.edit.cc:1413 f.widgets.cc:438 #: f.widgets.cc:684 msgid "Upright" msgstr "Adressar" #: f.batch.cc:152 f.batch.cc:564 f.batch.cc:1178 f.batch.cc:1669 #: f.repair.cc:118 f.widgets.cc:454 msgid "Sharpen" msgstr "Enfocar" #: f.batch.cc:154 f.batch.cc:1180 f.batch.cc:1671 msgid "amount" msgstr "quantitat" #: f.batch.cc:157 f.batch.cc:1183 f.batch.cc:1674 msgid "threshold" msgstr "umbral" #: f.batch.cc:280 msgid "file type not supported" msgstr "tipus d'arxiu no suportat" #: f.batch.cc:344 msgid "cannot create new file" msgstr "no es pot crear un nou arxiu" #: f.batch.cc:391 msgid "updating albums ..." msgstr "actualitzant àlbums ..." #: f.batch.cc:464 f.batch.cc:1456 f.batch.cc:1862 f.file.cc:1208 msgid "Select directory" msgstr "Seleccionar directori" #: f.batch.cc:513 msgid "" "new name/base/adder unreasonable\n" " e.g. newname ### 100 10\n" " or ... [oldname] ..." msgstr "" #: f.batch.cc:537 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "max. mida %d x %d no és raonable" #: f.batch.cc:556 #, c-format msgid "Convert %d image files" msgstr "Convertir %d imatges" #: f.batch.cc:557 msgid "Rename to" msgstr "Re-anomenar com" #: f.batch.cc:558 msgid "Convert to" msgstr "Convertir a" #: f.batch.cc:559 msgid "Resize within" msgstr "Redimmensionar dins de" #: f.batch.cc:560 msgid "Output to" msgstr "Sortida com" #: f.batch.cc:566 msgid "PROCEED?" msgstr "PROCEDIR?" #: f.batch.cc:720 f.widgets.cc:532 msgid "Batch Upright" msgstr "Adressar en lot" #: f.batch.cc:726 msgid "Survey all files" msgstr "Examinar tots els arxius" #: f.batch.cc:780 msgid "file cannot be read" msgstr "l'arxiu no pot ser llegit" #: f.batch.cc:898 msgid "cannot select both options" msgstr "no es poden sel·leccionar ambdues opcions" #: f.batch.cc:942 f.widgets.cc:533 msgid "Batch Delete/Trash" msgstr "Esborrar/paperera en lot" #: f.batch.cc:947 msgid "delete" msgstr "esborrar" #: f.batch.cc:950 msgid "trash" msgstr "paperera" #: f.batch.cc:1112 msgid "Batch Convert RAW Files (DCraw)" msgstr "Convertir en lot arxius RAW (DCraw)" #: f.batch.cc:1121 msgid "DCraw not installed" msgstr "DCraw no instal.lat" #: f.batch.cc:1156 f.batch.cc:1647 msgid "output location" msgstr "ubicació exterior" #: f.batch.cc:1161 f.batch.cc:1652 msgid "output file type" msgstr "tipus d'arxiu de sortida" #: f.batch.cc:1192 msgid "white balance" msgstr "balanç de blancs" #: f.batch.cc:1193 msgid "interpolation" msgstr "interpolació" #: f.batch.cc:1194 msgid "color space" msgstr "espai de color" #: f.batch.cc:1195 msgid "gamma curve" msgstr "corba gamma" #: f.batch.cc:1198 msgid "camera" msgstr "càmera" #: f.batch.cc:1199 msgid "fixed" msgstr "corretgit" #: f.batch.cc:1200 msgid "calculated" msgstr "calculat" #: f.batch.cc:1217 msgid "default" msgstr "per defecte" #: f.batch.cc:1223 msgid "defaults" msgstr "per defecte" #: f.batch.cc:1610 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Convertir en lot arxius RAW (RawTherapee)" #: f.batch.cc:1619 f.file.cc:428 msgid "Raw Therapee not installed" msgstr "RawTherapee no instal.lat" #: f.batch.cc:1959 msgid "start" msgstr "iniciar" #: f.batch.cc:1960 msgid "begin making a script file" msgstr "iniciar construcció d'un script" #: f.batch.cc:1962 msgid "close" msgstr "tancar" #: f.batch.cc:1963 msgid "finish making a script file" msgstr "acabant construcció d'un script" #: f.batch.cc:1965 msgid "run" msgstr "executar" #: f.batch.cc:1966 msgid "execute a script file" msgstr "executar un script" #: f.batch.cc:2020 msgid "script already started" msgstr "c¡script iniciat" #: f.batch.cc:2024 msgid "open new script file" msgstr "obrir nou script" #: f.batch.cc:2056 msgid "script file error" msgstr "error d'script" #: f.batch.cc:2061 #, c-format msgid "%s added to script" msgstr "%s afegit a l'script" #: f.batch.cc:2074 msgid "no script file was started" msgstr "no s'ha iniciat l'script" #: f.batch.cc:2082 msgid "script file closed" msgstr "script tancat" #: f.batch.cc:2101 msgid "script file is not closed" msgstr "no s'ha tancat l'script" #: f.batch.cc:2105 msgid "select script file to run" msgstr "sel·leccionar scrit a executar" #: f.batch.cc:2107 msgid "open script file" msgstr "obrir script" #: f.batch.cc:2114 msgid "unknown script file" msgstr "script desconegut" #: f.batch.cc:2121 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" "script: %s \n" " %s" #: f.batch.cc:2132 msgid "select image files to be processed" msgstr "sel·leccionar imatges a processar" #: f.batch.cc:2144 f.batch.cc:2193 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "error d'apertura: %s \n" " %s" #: f.batch.cc:2165 #, c-format msgid "unknown edit function: %s" msgstr "funció d'edició desconeguda: %s" #: f.batch.cc:2174 #, c-format msgid "load widgets failed: %s" msgstr "carregar complements que han fallat: %s" #: f.batch.cc:2203 #, c-format msgid "script file format error: %s" msgstr "error de format d'script: %s" #: f.batch.cc:2229 msgid "Brasero not installed" msgstr "Brasera no instal.lat" #: f.batch.cc:2318 f.widgets.cc:538 msgid "Find Duplicate Images" msgstr "Trobar imatges duplicades" #: f.batch.cc:2320 f.tools.cc:929 msgid "Thumbnail size" msgstr "Tamany de miniatura" #: f.batch.cc:2323 msgid "pixel difference" msgstr "diferència de píxels" #: f.batch.cc:2326 msgid "pixel count" msgstr "conteig de píxels" #: f.batch.cc:2329 msgid "Images:" msgstr "Imatges:" #: f.batch.cc:2330 msgid "searching ..." msgstr "buscant ..." #: f.batch.cc:2332 msgid "Duplicates:" msgstr "Duplicats:" #: f.batch.cc:2350 #, c-format msgid "only %d image thumbnails found" msgstr "trobades tant sols % imatges" #: f.bend.cc:86 f.widgets.cc:475 msgid "Unbend" msgstr "Redreçar" #: f.bend.cc:94 f.edit.cc:4267 f.effects.cc:4725 msgid "vertical" msgstr "vertical" #: f.bend.cc:95 f.edit.cc:4266 f.effects.cc:4721 msgid "horizontal" msgstr "horitzontal" #: f.bend.cc:96 msgid "linear" msgstr "lineal" #: f.bend.cc:99 msgid "curved" msgstr "curvat" #: f.bend.cc:367 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.bend.cc:384 msgid "Perspective Correction" msgstr "Corregir perspectiva" #: f.bend.cc:590 msgid "must have 4 corners" msgstr "ha de tenir 4 cantonades" #: f.bend.cc:712 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.bend.cc:725 f.widgets.cc:477 msgid "Warp area" msgstr "Deformar àrea" #: f.bend.cc:730 msgid "start warp" msgstr "iniciar deformació" #: f.bend.cc:788 msgid "no active Select Area" msgstr "Seleccionar àrea no actiu" #: f.bend.cc:1129 f.bend.cc:1443 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.bend.cc:1147 f.widgets.cc:478 msgid "Warp curved" msgstr "Deformació corba" #: f.bend.cc:1156 f.mashup.cc:1836 msgid "warp span" msgstr "intèrval de deformació" #: f.bend.cc:1461 f.widgets.cc:479 msgid "Warp linear" msgstr "Deformació lineal" #: f.bend.cc:1776 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.bend.cc:1792 f.widgets.cc:480 msgid "Warp affine" msgstr "Deformació afí" #: f.bend.cc:2138 msgid "Flatten Book Page Photo" msgstr "Aplanar foto d'una pàgina d'un llibre " #: f.bend.cc:2139 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.bend.cc:2142 msgid "Stretch curved-down surfaces:" msgstr "Adreçar superfícies curvades per sota" #: f.bend.cc:2197 msgid "top:" msgstr "adalt :" #: f.bend.cc:2200 msgid "bottom:" msgstr "abaix :" #: f.combine.cc:2108 f.combine.cc:2740 f.combine.cc:3396 f.combine.cc:3928 msgid "Select 2 to 9 files" msgstr "Seleccionar de 2 a 9 fitxers" #: f.combine.cc:2129 f.combine.cc:2761 f.combine.cc:3417 f.combine.cc:3949 msgid "Images are not all the same size" msgstr "Les imatges no són totes de la mateixa mida" #: f.combine.cc:2473 msgid "Adjust Image Contributions" msgstr "Ajustar contribucions de cada imatge" #: f.combine.cc:2477 msgid "dark pixels" msgstr "ombres" #: f.combine.cc:2479 msgid "light pixels" msgstr "llums" #: f.combine.cc:2481 msgid "file:" msgstr "fitxer:" #: f.combine.cc:2997 msgid "Paint and Warp Image" msgstr "Pintar i deformar imatge" #: f.combine.cc:3001 f.combine.cc:3646 f.combine.cc:6237 f.effects.cc:3912 #: f.widgets.cc:567 f.widgets.cc:590 f.widgets.cc:612 msgid "Image" msgstr "Imatge" #: f.combine.cc:3005 msgid "paint" msgstr "pintar" #: f.combine.cc:3006 msgid "warp" msgstr "deformar" #: f.combine.cc:3644 msgid "Select and Paint Image" msgstr "Seleccionar i pintar imatge" #: f.combine.cc:4120 msgid "Adjust Pixel Composition" msgstr "Ajustar la composició de pixels" #: f.combine.cc:4122 msgid "use average" msgstr "usar la mitja" #: f.combine.cc:4123 msgid "use median" msgstr "usar la mediana" #: f.combine.cc:4125 msgid "omit low pixel" msgstr "omitir píxel baix" #: f.combine.cc:4126 msgid "omit high pixel" msgstr "omitir píxel alt" #: f.combine.cc:4388 f.combine.cc:5494 msgid "Select 2 to 4 files" msgstr "Seleccionar de 2 a 4 fitxers" #: f.combine.cc:4463 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:4465 f.combine.cc:5571 msgid "no curve (scanned image)" msgstr "no hi ha corba (imatge escanejada)" #: f.combine.cc:4466 f.combine.cc:5572 msgid "Search for lens mm" msgstr "Cerca mm de la lent" #: f.combine.cc:4467 f.combine.cc:5573 msgid "Save lens mm → image EXIF" msgstr "Guardar focal en EXIF" #: f.combine.cc:4528 f.combine.cc:5634 msgid "Pre-align Images" msgstr "Pre-alinear imatges" #: f.combine.cc:4532 f.combine.cc:5638 msgid "lens mm" msgstr "mm de la lent" #: f.combine.cc:4537 f.combine.cc:5643 msgid "no auto warp" msgstr "no auto correcció" #: f.combine.cc:4539 f.combine.cc:5645 f.widgets.cc:445 f.widgets.cc:683 msgid "Resize" msgstr "Redimensionar" #: f.combine.cc:4540 f.combine.cc:5646 msgid "resize window" msgstr "redimensionar finestra" #: f.combine.cc:4548 f.combine.cc:5654 msgid "do not warp images during auto-alignment" msgstr "no deformar imatges mentre s'auto-alineen" #: f.combine.cc:4593 f.combine.cc:5699 msgid "use two images only" msgstr "utilitzar només dues imatges" #: f.combine.cc:4621 f.combine.cc:4825 f.combine.cc:5011 f.combine.cc:5725 #: f.combine.cc:5929 f.combine.cc:6115 msgid "Too little overlap, cannot align" msgstr "molt poc solpament, no es pot alinear" #: f.combine.cc:5089 f.combine.cc:6193 msgid "Match Brightness and Color" msgstr "Coincidència de brillantor i color" #: f.combine.cc:5134 msgid "Select image" msgstr "Sele ccionar imatge" #: f.combine.cc:5149 f.combine.cc:6252 msgid "auto color" msgstr "auto color" #: f.combine.cc:5150 f.combine.cc:6253 msgid "file color" msgstr "fitxer de color" #: f.combine.cc:5156 f.combine.cc:6259 msgid "mouse warp" msgstr "corregir amb el ratolí" #: f.combine.cc:5159 msgid "curved image" msgstr "imatge corba" #: f.combine.cc:5569 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:6469 msgid "pano tools (hugin) not installed" msgstr "Pano Tools (Hugin) no installat" #: f.combine.cc:6478 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:173 f.widgets.cc:437 f.widgets.cc:682 msgid "Trim/Rotate" msgstr "Retallar/Girar" #: f.edit.cc:185 f.edit.cc:586 msgid "ratio" msgstr "proporció" #: f.edit.cc:189 msgid "trim size:" msgstr "mida de retallat" #: f.edit.cc:194 msgid "Lock Ratio" msgstr "Bloquejar proporció" #: f.edit.cc:199 msgid "Customize" msgstr "Personalitzar" #: f.edit.cc:207 msgid "Rotate: degrees" msgstr "Girar : graus" #: f.edit.cc:209 msgid "auto-trim" msgstr "auto-retallar" #: f.edit.cc:582 msgid "Trim Buttons" msgstr "Botons de retallar" #: f.edit.cc:585 msgid "label" msgstr "etiqueta" #: f.edit.cc:1411 msgid "Upright Image" msgstr "Adressar imatge" #: f.edit.cc:1477 msgid "rotation unknown" msgstr "ratació desconeguda" #: f.edit.cc:2029 f.widgets.cc:441 f.widgets.cc:686 msgid "Retouch Combo" msgstr "Retocar llum i color " #: f.edit.cc:2050 msgid "Amplifier" msgstr "Amplificar" #: f.edit.cc:2051 fotoxx.h:1091 msgid "Brightness" msgstr "Brillantor" #: f.edit.cc:2052 f.effects.cc:3364 fotoxx.h:1099 msgid "Contrast" msgstr "Contrast" #: f.edit.cc:2053 msgid "Low Color" msgstr "Color -" #: f.edit.cc:2054 msgid "Warmer" msgstr "Càlid" #: f.edit.cc:2055 f.effects.cc:1011 msgid "Dark Areas" msgstr "Ombres" #: f.edit.cc:2064 msgid "Max." msgstr "Max" #: f.edit.cc:2065 f.edit.cc:2066 f.edit.cc:2067 msgid "High" msgstr "+" #: f.edit.cc:2068 msgid "Cooler" msgstr "Fred" #: f.edit.cc:2069 msgid "Bright" msgstr "Llums" #: f.edit.cc:2072 f.tools.cc:1631 msgid "Brightness Distribution" msgstr "Distribució de la brillantor" #: f.edit.cc:2075 msgid "Click for white balance or black level" msgstr "Clic per balanç de blanc o punt negre" #: f.edit.cc:2078 msgid "Settings File" msgstr "Ajustaments" #: f.edit.cc:2082 msgid "recall previous settings used" msgstr "recuperar ajustaments usats anteriorment" #: f.edit.cc:2726 msgid "Adjust Brightness Distribution" msgstr "Ajustar histograma" #: f.edit.cc:2765 msgid "Low Cutoff" msgstr "Tall baix" #: f.edit.cc:2766 msgid "High Cutoff" msgstr "Tall alt" #: f.edit.cc:2767 msgid "Low Flatten" msgstr "Aplanat baix" #: f.edit.cc:2768 msgid "Mid Flatten" msgstr "Aplanat mitjà" #: f.edit.cc:2769 msgid "High Flatten" msgstr "Aplanat alt" #: f.edit.cc:2770 msgid "Low Stretch" msgstr "Tram baix" #: f.edit.cc:2771 msgid "Mid Stretch" msgstr "Tram mitjà" #: f.edit.cc:2772 msgid "High Stretch" msgstr "Tram alt" #: f.edit.cc:3102 msgid "Zonal Flatten Brightness" msgstr "Aplanarn brillantor zonal" #: f.edit.cc:3135 msgid "Zones" msgstr "Zones" #: f.edit.cc:3142 msgid "Deband Dark" msgstr "Eliminar bandes fosques" #: f.edit.cc:3145 msgid "Deband Bright" msgstr "Eliminar bandes lluminoses" #: f.edit.cc:3594 f.widgets.cc:444 f.widgets.cc:689 msgid "Tone Mapping" msgstr "Mapa tonal" #: f.edit.cc:3625 msgid "low" msgstr "baix" #: f.edit.cc:3627 msgid "high" msgstr "alt" #: f.edit.cc:3630 msgid "Amplify" msgstr "Ampliar" #: f.edit.cc:4034 msgid "Resize Image" msgstr "Redimensionar imatge" #: f.edit.cc:4059 msgid "W/H Ratio:" msgstr "Relació amplada/alçada:" #: f.edit.cc:4061 msgid "Lock" msgstr "Bloquejar" #: f.edit.cc:4063 msgid "use previous settings" msgstr "etilitzar especificacions anteriors" #: f.edit.cc:4262 f.widgets.cc:446 msgid "Flip" msgstr "Voltejar" #: f.edit.cc:4368 msgid "+Version" msgstr "+Version" #: f.edit.cc:4391 msgid "Write Text on Image" msgstr "Escriure text en una imatge" #: f.edit.cc:4392 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:4441 f.mashup.cc:2437 msgid "Text" msgstr "Text" #: f.edit.cc:4446 msgid "Use metadata key" msgstr "Usar clau de metadades" #: f.edit.cc:4451 msgid "Use text file" msgstr "Usar arxiu de text" #: f.edit.cc:4470 f.mashup.cc:2455 msgid "text" msgstr "text" #: f.edit.cc:4471 f.edit.cc:5309 f.mashup.cc:2456 f.mashup.cc:2798 msgid "backing" msgstr "fons" #: f.edit.cc:4472 f.edit.cc:5310 f.mashup.cc:2457 f.mashup.cc:2799 msgid "outline" msgstr "contorn" #: f.edit.cc:4473 f.edit.cc:5311 f.mashup.cc:2458 f.mashup.cc:2800 msgid "shadow" msgstr "ombra" #: f.edit.cc:4499 msgid "save to current file" msgstr "guardar en l'arxiu actual" #: f.edit.cc:4500 f.file.cc:1909 msgid "save as new file version" msgstr "guardar com una nova versió" #: f.edit.cc:4501 msgid "" "save to current file \n" "open next file with same text" msgstr "" "guardar en l'arxiu actual \n" "obrir següent arxiu amb el mateix text" #: f.edit.cc:4662 f.mashup.cc:2557 f.tools.cc:1068 msgid "select font" msgstr "seleccionar tipus de lletra" #: f.edit.cc:4926 msgid "text file is defective" msgstr "arxiu de text és defectuós" #: f.edit.cc:5249 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:5280 msgid "Write Line or Arrow on Image" msgstr "Dibuixar línia o fletxa en una imatge" #: f.edit.cc:5288 f.mashup.cc:2777 msgid "Line length" msgstr "Longitut de línia" #: f.edit.cc:5295 f.mashup.cc:2784 msgid "Arrow head" msgstr "Punta de fletxa" #: f.edit.cc:5308 f.mashup.cc:2797 msgid "line" msgstr "linea" #: f.edit.cc:5337 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:5926 f.widgets.cc:449 msgid "Paint Edits" msgstr "Editar pintant" #: f.edit.cc:5933 f.edit.cc:6162 fotoxx-15.11.cc:3179 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "Àrea seleccionada no pot ser guardada.\n" "Continuar?" #: f.edit.cc:5941 f.edit.cc:6170 f.tools.cc:2157 msgid "Edit function must be active" msgstr "La funció d'editar pintant ha d'estar activada" #: f.edit.cc:5946 msgid "Cannot use Paint Edits" msgstr "No es pot usar Editar pintant" #: f.edit.cc:5975 msgid "power: center" msgstr "força: centre" #: f.edit.cc:5980 msgid "reset area" msgstr "reiniciar àrea" #: f.edit.cc:6155 f.widgets.cc:450 msgid "Leverage Edits" msgstr "Edició per nivells" #: f.edit.cc:6156 msgid "Edit Function Amplifier" msgstr "Amplificador de la funció d'edició" #: f.edit.cc:6175 msgid "Cannot use Leverage Edits" msgstr "No es pot usar Edició per nivells" #: f.edit.cc:6208 msgid "minimum" msgstr "ombres" #: f.edit.cc:6210 msgid "maximum" msgstr "llums" #: f.edit.cc:6510 msgid "Edit Plugins" msgstr "Editar complements (Plugins)" #: f.edit.cc:6511 msgid "Edit plugins menu" msgstr "Editar menú de plugins" #: f.edit.cc:6519 msgid "Run as Fotoxx edit function" msgstr "Obrir com una funció d'edició de Fotoxx" #: f.edit.cc:6757 msgid "Plugin working ..." msgstr "Plugin treballant ..." #: f.edit.cc:6766 msgid "plugin failed" msgstr "errada de complement" #: f.effects.cc:64 msgid "Set color depth to 1-16 bits" msgstr "Ajustar la profunditat de color a 1-16 bits" #: f.effects.cc:76 msgid "Set Color Depth" msgstr "Ajustar la profunditat de color" #: f.effects.cc:220 msgid "Convert to Sketch" msgstr "Convertir a esbós" #: f.effects.cc:296 msgid "Clip Level" msgstr "Nivell de tall" #: f.effects.cc:300 msgid "Algorithm" msgstr "Algoritme" #: f.effects.cc:305 msgid "Foreground" msgstr "Primer pla" #: f.effects.cc:308 msgid "Background" msgstr "Fons" #: f.effects.cc:714 f.widgets.cc:486 msgid "Line Drawing" msgstr "Dibuix amb línees" #: f.effects.cc:727 msgid "black/white" msgstr " negre/blanc" #: f.effects.cc:1005 f.widgets.cc:487 msgid "Color Drawing" msgstr "Dibuix amb colors" #: f.effects.cc:1012 msgid "Bright Areas" msgstr "Llums" #: f.effects.cc:1266 f.widgets.cc:488 msgid "Graduated Blur" msgstr "Desenfocament graduat" #: f.effects.cc:1270 msgid "Contrast Limit" msgstr "Límit de contrast" #: f.effects.cc:1273 f.repair.cc:775 msgid "Blur Radius" msgstr "Radi de desenfocament" #: f.effects.cc:1487 msgid "Simulate Embossing" msgstr "Simular un relleu" #: f.effects.cc:1504 msgid "depth" msgstr "profunditat" #: f.effects.cc:1706 msgid "Simulate Tiles" msgstr "Simular un mosaic" #: f.effects.cc:1710 msgid "tile size" msgstr "mida de la rajola" #: f.effects.cc:1713 msgid "tile gap" msgstr "separació de lles rajoles" #: f.effects.cc:1716 msgid "3D depth" msgstr "profunditat 3D" #: f.effects.cc:1940 msgid "Convert Image to Dots" msgstr "Convertrr la imatge a punts" #: f.effects.cc:1944 msgid "dot size" msgstr "mida de punt" #: f.effects.cc:2169 msgid "Simulate Painting" msgstr "Simular una Pintura" #: f.effects.cc:2173 msgid "color depth" msgstr "profunditat de color" #: f.effects.cc:2177 msgid "patch area goal" msgstr "mida de l'àrea de color" #: f.effects.cc:2181 msgid "req. color match" msgstr "correspondència de color requerida" #: f.effects.cc:2185 msgid "borders" msgstr "vores" #: f.effects.cc:2758 f.widgets.cc:493 msgid "Vignette" msgstr "Vinyetat" #: f.effects.cc:3107 msgid "Add Texture" msgstr "Afegir textura" #: f.effects.cc:3324 msgid "Background Pattern" msgstr "Patrò de fons" #: f.effects.cc:3328 msgid "Pattern File:" msgstr "Arxiu de patrò" #: f.effects.cc:3333 msgid "Geometry" msgstr "Geometria" #: f.effects.cc:3334 msgid "Calculate" msgstr "Calcular" #: f.effects.cc:3336 msgid "Zoom" msgstr "Zoom" #: f.effects.cc:3347 f.widgets.cc:495 msgid "Pattern" msgstr "Patrò" #: f.effects.cc:3354 msgid "Overlap" msgstr "Superposició" #: f.effects.cc:3361 msgid "Opacity" msgstr "Opacitat" #: f.effects.cc:3429 msgid "choose pattern tile" msgstr "Escollir arxiu patrò" #: f.effects.cc:3816 msgid "Create Mosaic" msgstr "Crear mosaïc" #: f.effects.cc:3902 msgid "Tile" msgstr "Rajola" #: f.effects.cc:3910 f.widgets.cc:490 msgid "Tiles" msgstr "Rajoles" #: f.effects.cc:3916 msgid "Tile blending" msgstr "Mescla de rajoles" #: f.effects.cc:3996 #, c-format msgid "exceeded max. tiles: %d" msgstr "excedit max. arxius: %d" #: f.effects.cc:4011 #, c-format msgid "only %d tile images found" msgstr "trobades sols %d imatges" #: f.effects.cc:4404 f.widgets.cc:497 msgid "Custom Kernel" msgstr "Matriu de convolució" #: f.effects.cc:4408 msgid "Kernel size" msgstr "Tamany de matriu" #: f.effects.cc:4411 msgid "Divisor" msgstr "Divisor" #: f.effects.cc:4414 msgid "Data file" msgstr "Arxiu de dades" #: f.effects.cc:4450 fotoxx-15.11.cc:3440 msgid "Load settings from file" msgstr "Carregar ajustaments des d'un arxiu" #: f.effects.cc:4711 f.widgets.cc:498 msgid "Make Waves" msgstr "Ones" #: f.effects.cc:4718 msgid "wavelength" msgstr "longitut d'ona" #: f.effects.cc:4719 msgid "amplitude" msgstr "amplitut" #: f.effects.cc:4720 msgid "variance" msgstr "variança" #: f.effects.cc:4731 msgid "perspective" msgstr "prespectiva​" #: f.effects.cc:4905 msgid "Pull image using the mouse." msgstr "Empènyer la imatge usant el ratolí" #: f.effects.cc:4926 f.widgets.cc:499 msgid "Directed Blur" msgstr "Desenfocament direccional" #: f.effects.cc:4930 msgid "blur span" msgstr "quantitat de desenfocament" #: f.effects.cc:4933 msgid "intensity" msgstr "intensitat" #: f.effects.cc:5099 f.widgets.cc:500 msgid "Spherical Projection" msgstr "Projecció esfèrica" #: f.effects.cc:5143 msgid "Magnify" msgstr "Augmentar" #: f.file.cc:186 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:203 f.widgets.cc:572 msgid "File" msgstr "Arxiu" #: f.file.cc:357 msgid "UFraw not installed" msgstr "UFRaw no instal.lat" #: f.file.cc:373 f.widgets.cc:691 msgid "Open RAW file (ufraw)" msgstr "Obrir fitxer RAW (ufraw)" #: f.file.cc:378 f.file.cc:449 msgid "RAW type not registered in User Settings" msgstr "tipus RAW no registrat en Configuració d'usuari" #: f.file.cc:444 f.widgets.cc:692 msgid "Open RAW file (Raw Therapee)" msgstr "Obrir arxiu RAW amb RawTherapee" #: f.file.cc:541 f.widgets.cc:395 msgid "Open Image File" msgstr "Obrir arxiu d'imatge" #: f.file.cc:560 msgid "unknown file type" msgstr "tipus d'arxiu desconegut" #: f.file.cc:753 msgid "Create Blank Image" msgstr "Crear imatge buida" #: f.file.cc:755 msgid "file name" msgstr "nom d'arxiu" #: f.file.cc:788 msgid "supply a file name" msgstr "proporcionar nom d'àlbum" #: f.file.cc:934 f.widgets.cc:400 msgid "Rename Image File" msgstr "Re-anomenar arxiu d'imatge" #: f.file.cc:941 msgid "Old Name" msgstr "antic nom" #: f.file.cc:951 msgid "previous name" msgstr "nom anterior" #: f.file.cc:952 msgid "add 1" msgstr "afegir 1" #: f.file.cc:1120 msgid "Copy Image File" msgstr "Copiar imatge" #: f.file.cc:1121 msgid "Move Image File" msgstr "Moure imarge" #: f.file.cc:1164 msgid "new location" msgstr "nova ubicació" #: f.file.cc:1236 msgid "new location is not a directory" msgstr "la nova ubicació no és un directori" #: f.file.cc:1276 f.file.cc:1518 f.file.cc:1656 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "l'esborrat ha fallat: \n" " %s" #: f.file.cc:1363 f.widgets.cc:401 msgid "Trash Image File" msgstr "Imatge a la paperera" #: f.file.cc:1364 f.file.cc:1571 msgid "(automatic step to next image)" msgstr "(passi automàtic a la nova imatge)" #: f.file.cc:1432 msgid "" "Linux standard trash does not work. \n" "Desktop trash folder will be created." msgstr "" "Paperera estàndard de Linux no funciona. \n" "Es crearà una carpeta de paperera." #: f.file.cc:1435 msgid "" "Linux and Desktop trash do not work. \n" "Permanently delete the image file?" msgstr "" "Les papereres de Linux i del Escriptori no funcionen. \n" "Esborrar permanenment la imatge?" #: f.file.cc:1438 #, c-format msgid "Cannot create trash folder: %s" msgstr "No es pot crear carpeta de paperera: %s" #: f.file.cc:1450 msgid "Move read-only file to trash?" msgstr "Moure a la paperera fitxers de nomès lectura?" #: f.file.cc:1505 #, c-format msgid "error: %s" msgstr "error: %s" #: f.file.cc:1534 f.file.cc:1673 f.file.cc:2508 msgid "no more images" msgstr "no hi ha més imatges" #: f.file.cc:1570 msgid "Delete Image File - CANNOT BE REVERSED" msgstr "Esborrar imatge - NO ES POT TORNAR ENRERA" #: f.file.cc:1646 msgid "Delete read-only file?" msgstr "Esborrar arxius de només lectura?" #: f.file.cc:1891 msgid "Save Image File" msgstr "Guardar imatge" #: f.file.cc:1901 msgid "new version" msgstr "nova versió" #: f.file.cc:1902 msgid "new file" msgstr "nou arxiu" #: f.file.cc:1903 msgid "replace file" msgstr "substituïr arxiu" #: f.file.cc:1910 f.file.cc:2281 msgid "save as new file name or type" msgstr "guardar com un nou nom o tipus d'arxiu" #: f.file.cc:1912 msgid "replace old file (OVERWRITE)" msgstr "substituïr arxiu anterior (SOBRE-ESCRIURE)" #: f.file.cc:1950 f.file.cc:2057 msgid "cannot save as RAW type" msgstr "no es pot guardar com a RAW" #: f.file.cc:2051 msgid "" "Transparency map will be lost.\n" "save to PNG file to retain." msgstr "" "Es perdrà el mapa de transparènciaguardar com arxiu .PNG per conservar-lo" #: f.file.cc:2191 msgid "Unable to copy EXIF/IPTC data" msgstr "No puc copiar dades EXIF/IPTC" #: f.file.cc:2299 f.file.cc:2302 msgid "make current" msgstr "fer actual" #: f.file.cc:2300 msgid "(new file becomes current file)" msgstr "(el nou arxiu serà l'arxiu actual)" #: f.file.cc:2409 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Sobre-escriure arxiu? \n" " %s" #: f.file.cc:2555 f.widgets.cc:554 msgid "Quick Start" msgstr "Inici ràpid" #: f.file.cc:2558 f.widgets.cc:555 msgid "User Guide" msgstr "Guia d'usuari" #: f.file.cc:2561 f.widgets.cc:556 msgid "User Guide Changes" msgstr "Canvis a la guia d'usuari" #: f.file.cc:2564 f.widgets.cc:558 msgid "README" msgstr "LLEGEIX-ME" #: f.file.cc:2567 f.widgets.cc:557 msgid "Edit Functions Summary" msgstr "Resum de les funcions d'edició" #: f.file.cc:2570 f.widgets.cc:559 msgid "Change Log" msgstr "Registre de canvis" #: f.file.cc:2573 f.widgets.cc:560 msgid "Log File" msgstr "Arxiu de registre" #: f.file.cc:2576 f.widgets.cc:561 msgid "Translations" msgstr "Traduccions" #: f.file.cc:2579 f.widgets.cc:562 msgid "Home Page" msgstr "Pàgina web de Fotoxx" #: f.file.cc:2582 f.widgets.cc:563 msgid "About" msgstr "Sobre Fotoxx " #: f.file.cc:2586 f.widgets.cc:584 f.widgets.cc:606 fotoxx.h:1123 msgid "Help" msgstr "Ajuda" #: f.file.cc:2739 f.file.cc:2808 #, c-format msgid "file not found: %s" msgstr "arxiu no trobat: %s" #: f.file.cc:2745 f.file.cc:2814 #, c-format msgid "file type not supported: %s" msgstr "tipus d'arxiu no suportaat: %s" #: f.gallery.cc:918 f.gallery.cc:1007 f.widgets.cc:604 msgid "Scroll" msgstr "Desplaçar" #: f.gallery.cc:922 msgid "Sync" msgstr "Sync" #: f.gallery.cc:932 f.repair.cc:5845 f.repair.cc:5850 fotoxx.h:1144 msgid "Open" msgstr "Obrir" #: f.gallery.cc:933 msgid "change directory" msgstr "canviar directori" #: f.gallery.cc:941 msgid "GoTo" msgstr "Anar a" #: f.gallery.cc:947 f.widgets.cc:599 msgid "Sort" msgstr "Ordenar" #: f.gallery.cc:953 f.gallery.cc:1517 f.gallery.cc:1518 f.gallery.cc:1520 #: f.widgets.cc:597 msgid "Zoom+" msgstr "Zoom +" #: f.gallery.cc:963 f.gallery.cc:1519 f.gallery.cc:1521 f.widgets.cc:598 msgid "Zoom-" msgstr "Zoom -" #: f.gallery.cc:975 f.gallery.cc:1525 msgid "Row↑" msgstr "Fila ↑" #: f.gallery.cc:983 f.gallery.cc:1526 msgid "Row↓" msgstr "Fila ↓" #: f.gallery.cc:991 f.gallery.cc:1523 f.gallery.cc:1530 f.widgets.cc:602 msgid "Page↑" msgstr "Pàgina ↑" #: f.gallery.cc:999 f.gallery.cc:1524 f.gallery.cc:1531 f.widgets.cc:603 msgid "Page↓" msgstr "Pàgina ↓" #: f.gallery.cc:1013 f.gallery.cc:1528 f.widgets.cc:600 msgid "First" msgstr "Primera" #: f.gallery.cc:1014 f.gallery.cc:1529 f.widgets.cc:601 msgid "Last" msgstr "Última" #: f.gallery.cc:1127 msgid "Choose image directory" msgstr "Escollir directori d'imatge" #: f.gallery.cc:1134 f.gallery.cc:1148 msgid "recent" msgstr " recent" #: f.gallery.cc:1135 f.gallery.cc:1153 msgid "newest" msgstr "més noves" #: f.gallery.cc:1217 msgid "no albums found" msgstr "No trobats àlbums" #: f.gallery.cc:1224 msgid "Choose album" msgstr "Escollir àlbums" #: f.gallery.cc:1274 msgid "Gallery Sort" msgstr "Ordenar galeria" #: f.gallery.cc:1278 msgid "File Name" msgstr "Nom d'arxiu" #: f.gallery.cc:1279 msgid "File Mod Date/Time" msgstr "Mode d'arxiu date/hora" #: f.gallery.cc:1280 msgid "Photo Date/Time (EXIF)" msgstr "Data/hora de la foto (EXIF)" #: f.gallery.cc:1282 msgid "ascending" msgstr "ascendent" #: f.gallery.cc:1283 msgid "descending" msgstr " descendent" #: f.gallery.cc:2212 fotoxx.h:1169 msgid "Select Files" msgstr "Seleccionar arxius" #: f.gallery.cc:2672 msgid "Click list position. Click thumbnail to add." msgstr "Clic una posició de la llista. Clic una miniatura per afegir" #: f.gallery.cc:2698 f.gallery.cc:2946 msgid "Edit Bookmarks" msgstr "Editar marcadors" #: f.gallery.cc:2873 msgid "unable to save bookmarks file" msgstr "no puc guardar arxiu de marcadors" #: f.gallery.cc:2946 msgid "Go To Bookmark" msgstr "Anar a un marcador" #: f.gallery.cc:3003 f.meta.cc:2844 f.repair.cc:7473 msgid "file not found" msgstr "arxiu no trobat" #: 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:241 msgid "choose layout file" msgstr "escollir arxiu de capa" #: 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:417 msgid "Edit Images" msgstr "Editar imatges" #: f.mashup.cc:418 f.mashup.cc:2432 msgid "Edit Text" msgstr "Editar text" #: f.mashup.cc:419 msgid "Edit Line" msgstr "Editar text" #: f.mashup.cc:420 msgid "Rescale" msgstr "Re-escalar" #: f.mashup.cc:423 msgid "add or edit images" msgstr "afegir o editar imatges" #: f.mashup.cc:424 msgid "add or edit text" msgstr "afegir o editar text" #: f.mashup.cc:425 msgid "add or edit lines/arrows" msgstr "afegir o editar línees/fletxes" #: f.mashup.cc:426 msgid "change project scale" msgstr "canviar escala del projecte" #: f.mashup.cc:427 msgid "project complete" msgstr "projecte complert" #: f.mashup.cc:428 msgid "cancel project" msgstr "cancel.lar projecte" #: f.mashup.cc:446 msgid "rescale project" msgstr "re-escalar projecte" #: f.mashup.cc:501 msgid "save Mashup output file" msgstr "desar arxiu de sortida del fotomuntatge" #: f.mashup.cc:512 msgid "save Mashup project file?" msgstr "desar arxiu de projecte del fotomuntatge" #: f.mashup.cc:524 msgid "delete Mashup project file?" msgstr "esborrar arxiu projecte del fotomuntatge?" #: f.mashup.cc:583 msgid "Open Project" msgstr "Obrir projecte" #: f.mashup.cc:612 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "requerida imatge de capa \n" " %s" #: f.mashup.cc:669 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "requerida imatge sobreposada \n" " %s" #: f.mashup.cc:974 msgid "project file is defective" msgstr "arxiu de projecte defectuós" #: f.mashup.cc:1004 msgid "Save Project" msgstr "Guardar projecte" #: f.mashup.cc:1145 msgid "layout exceeds 2 gigabytes" msgstr "la composició excedeix 2 gigabytes" #: f.mashup.cc:1219 msgid "Click image to select, drag image to move." msgstr "Clic en la imatge per seleccionar, arrossegar imatge per moure-la" #: f.mashup.cc:1220 msgid "Make black margins transparent" msgstr "Fer transparents els marges negres" #: f.mashup.cc:1259 msgid "Current image:" msgstr "Imatge actual:" #: f.mashup.cc:1263 msgid "Cycle through images:" msgstr "Recorregut per les imatges:" #: f.mashup.cc:1270 msgid "Scale" msgstr "Escalar" #: f.mashup.cc:1279 msgid "Stacking Order" msgstr "Ordre de pila" #: f.mashup.cc:1280 msgid "raise" msgstr "pujar" #: f.mashup.cc:1281 msgid "lower" msgstr "baixar" #: f.mashup.cc:1284 msgid "Base Transparency" msgstr "Transparència" #: f.mashup.cc:1288 msgid "Var. Transparency" msgstr "Variar transparència" #: f.mashup.cc:1289 msgid "Paint" msgstr "Pintar" #: f.mashup.cc:1292 msgid "Bend and fine-align" msgstr "Combinar i alineació fina" #: f.mashup.cc:1293 msgid "Warp" msgstr "Deformar" #: f.mashup.cc:1302 msgid "Margins" msgstr "Marges" #: f.mashup.cc:1303 msgid "Hard" msgstr "Fort" #: f.mashup.cc:1304 f.repair.cc:5441 msgid "Blend" msgstr "Barrejar" #: f.mashup.cc:1318 msgid "add images to layout" msgstr "afegir imatges a la capa" #: f.mashup.cc:1557 msgid "Paint Image Transparencies" msgstr "Pintar transparències de la imatge" #: f.mashup.cc:1575 msgid "Gradual" msgstr "Gradual" #: f.mashup.cc:1580 fotoxx.h:1148 msgid "Power" msgstr "Força" #: f.mashup.cc:1814 msgid "Pull on the image with the mouse." msgstr "Empènyer la imatge amb el ratolí" #: f.mashup.cc:1830 msgid "Warp Image" msgstr "Deformar imatge" #: f.mashup.cc:2263 #, c-format msgid "exceeded %d images" msgstr "excedit %d imatges" #: f.mashup.cc:2405 msgid "Enter text, [Add] to layout, edit properties." msgstr "Afegir text, [Afegir] a la capa, editar propietats" #: f.mashup.cc:2485 msgid "Text File:" msgstr "arxiu de text:" #: f.mashup.cc:2489 msgid "add entered text to layout" msgstr "afegir text a la capa" #: f.mashup.cc:2618 msgid "click position to add text" msgstr "clic en la posició per afegir text" #: f.mashup.cc:2624 #, c-format msgid "exceeded %d text entries" msgstr "excedit%d entrades de text" #: f.mashup.cc:2747 msgid "Set line properties, [Add] to layout, edit." msgstr "Ajustar propietats de línia, [Afegir] a la capa, editar." #: f.mashup.cc:2771 msgid "Edit Line/Arrow" msgstr "Editar linea/fletxa" #: f.mashup.cc:2826 msgid "add line/arrow to layout" msgstr "afegir línia/fletxa a la capa" #: f.mashup.cc:2919 msgid "click position to add line" msgstr "Clic en la posició per afegir línia" #: f.mashup.cc:2925 #, c-format msgid "exceeded %d line entries" msgstr "excedides %d entrades de línia" #: f.meta.cc:180 f.widgets.cc:669 msgid "View Metadata" msgstr "Veure metadades" #: f.meta.cc:430 f.meta.cc:1807 f.widgets.cc:412 f.widgets.cc:670 msgid "Edit Metadata" msgstr "Editar metadades" #: f.meta.cc:433 f.meta.cc:3819 msgid "save metadata to file" msgstr "guardar metadades en l'arxiu" #: f.meta.cc:442 msgid "Image Date" msgstr "Data de la imatge" #: f.meta.cc:445 msgid "Time" msgstr "Hora" #: f.meta.cc:451 msgid "Rating (stars):" msgstr "Calificació (estrellas)" #: f.meta.cc:463 msgid "Caption" msgstr "Títol" #: f.meta.cc:469 msgid "Comments" msgstr "Comentaris" #: f.meta.cc:477 msgid "Enter New Tag" msgstr "Introduïr nova etiqueta" #: f.meta.cc:484 f.meta.cc:5003 msgid "Matching Tags" msgstr "Construïnt etiquetes" #: f.meta.cc:492 msgid "Image Tags" msgstr "Etiquetes d'imatge" #: f.meta.cc:498 msgid "Recent Tags" msgstr "etiquetes recents" #: f.meta.cc:507 f.meta.cc:2029 f.meta.cc:5010 msgid "Defined Tags Category" msgstr "Categoria d'etiquetes definida" #: f.meta.cc:898 msgid "date format is YYYY-MM-DD" msgstr "format de data es AAAA-MM-DD" #: f.meta.cc:902 msgid "date is invalid" msgstr "data no vàlida" #: f.meta.cc:936 msgid "time format is HH:MM [:SS]" msgstr "fomat d'hora es HH:MM [:SS]" #: f.meta.cc:940 msgid "time is invalid" msgstr "hora no vàlida" #: f.meta.cc:1025 fotoxx.h:1133 msgid "Manage Tags" msgstr "Gestionar etiquetes" #: f.meta.cc:1025 msgid "orphan tags" msgstr "etiquetes òrfenes" #: f.meta.cc:1029 msgid "category" msgstr "categoria" #: f.meta.cc:1032 msgid "tag" msgstr "etiqueta" #: f.meta.cc:1039 msgid "Defined Tags:" msgstr "etiquetes definides:" #: f.meta.cc:1811 f.meta.cc:2474 msgid "key name" msgstr "nom de clau" #: f.meta.cc:1813 f.meta.cc:2475 msgid "key value" msgstr "valor de clau" #: f.meta.cc:1900 f.widgets.cc:414 msgid "Delete Metadata" msgstr "Esborrar metadades" #: f.meta.cc:1902 fotoxx.h:1082 msgid "All" msgstr "Tot" #: f.meta.cc:1903 msgid "One Key:" msgstr "Una clau:" #: f.meta.cc:2006 f.widgets.cc:415 f.widgets.cc:540 msgid "Batch Add/Remove Tags" msgstr "Afegir/eliminar etiquetes en lot" #: f.meta.cc:2017 msgid "tags to add" msgstr "Etiquetes per afegir" #: f.meta.cc:2018 msgid "tags to remove" msgstr "etiquetes per eliminar" #: f.meta.cc:2102 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " massa etiquetes" #: f.meta.cc:2217 msgid "specify files and tags" msgstr "especificar arxius i etiquetes" #: f.meta.cc:2277 msgid "tag names file" msgstr "arxiu de noms d'etiqueta" #: f.meta.cc:2349 #, 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:2461 msgid "Batch Metadata" msgstr "Metadades en lot" #: f.meta.cc:2468 msgid "Short List" msgstr "Llista reduïda" #: f.meta.cc:2469 msgid "Full List" msgstr "Llista completa" #: f.meta.cc:2511 msgid "enter key names" msgstr "introduïr noms clau" #: f.meta.cc:2517 fotoxx.h:1141 msgid "no files selected" msgstr "no hi han arxius seleccionats" #: f.meta.cc:2643 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:3276 f.meta.cc:4011 f.meta.cc:4277 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "latitud/longitud errònia: %s %s" #: f.meta.cc:3320 f.meta.cc:3682 f.meta.cc:4486 f.meta.cc:4894 msgid "-noindex in use, disabled" msgstr "-noindex en ús, ​deshabilitar" #: f.meta.cc:3336 msgid "choose map file" msgstr "escollir arxiu de mapes" #: f.meta.cc:3444 msgid "" "fotoxx-maps package not installed \n" "(see http://kornelix.com/packages and /tarballs)" msgstr "" "paquet fotoxx-maps no instal·lat \n" "(veure http://kornelix.com/packages i /tarballs)" #: f.meta.cc:3449 #, c-format msgid "map file %s is missing" msgstr "es necessita arxiu de mapes %s" #: f.meta.cc:3453 #, 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:3731 msgid "No matching images found" msgstr "No s'han trobat imatges coincidents" #: f.meta.cc:3771 msgid "search range (km)" msgstr "buscar rang (km)" #: f.meta.cc:3804 f.widgets.cc:418 f.widgets.cc:671 msgid "Edit Geotags" msgstr "Editar geoetiquetes" #: f.meta.cc:3805 msgid "Geocoding web service courtesy of" msgstr "Servei web de geo-codis cortesia de" #: f.meta.cc:3821 f.meta.cc:4060 f.meta.cc:5783 msgid "city" msgstr "ciutat" #: f.meta.cc:3824 f.meta.cc:4063 f.meta.cc:5786 msgid "country" msgstr "pais" #: f.meta.cc:3906 f.meta.cc:4194 msgid "city not found" msgstr "ciutat no trobada" #: f.meta.cc:4027 f.widgets.cc:419 f.widgets.cc:543 msgid "Batch Add Geotags" msgstr "Afegir geoetiquetes" #: f.meta.cc:4256 msgid "" "data is incomplete \n" " proceed?" msgstr "" "les dades són incompletas \n" " procedir?" #: f.meta.cc:4313 msgid "choose city" msgstr "escollir ciutat" #: f.meta.cc:4400 msgid "not found" msgstr "no trobada" #: f.meta.cc:4401 msgid "city and country required" msgstr "és necessari ciutat i pais" #: f.meta.cc:4501 msgid "Report Geotag Groups" msgstr "Informe de grups de geoetiquetes" #: f.meta.cc:4502 msgid "Group by country" msgstr "Agrupar per pais" #: f.meta.cc:4503 msgid "Group by country/city" msgstr "Agrupar per pais/ciutat" #: f.meta.cc:4504 msgid "Group by country/city/date" msgstr "Agrupar pais/ciutat/data" #: f.meta.cc:4507 msgid "Combine within" msgstr "Conbinar dins de" #: f.meta.cc:4509 msgid "days" msgstr "dies" #: f.meta.cc:4625 msgid "geotag groups" msgstr "grups de geo-etiquetes" #: f.meta.cc:4931 msgid "Search Image Metadata" msgstr "Buscar metadades de la imatge" #: f.meta.cc:4935 msgid "images to search:" msgstr "imatges a buscar:" #: f.meta.cc:4936 msgid "all" msgstr "tot" #: f.meta.cc:4937 msgid "current set only" msgstr "omés selecció actual" #: f.meta.cc:4940 msgid "matching images:" msgstr "imatges coïncidents" #: f.meta.cc:4941 msgid "new set" msgstr "nova selecció" #: f.meta.cc:4942 msgid "add to set" msgstr "afegir a la selecció" #: f.meta.cc:4943 msgid "remove" msgstr "eliminar" #: f.meta.cc:4946 msgid "report type:" msgstr "tipus d'informe" #: f.meta.cc:4947 msgid "gallery" msgstr "galeria" #: f.meta.cc:4948 fotoxx-15.11.cc:791 msgid "metadata" msgstr "metadades" #: f.meta.cc:4954 msgid "date range" msgstr "rand de dates" #: f.meta.cc:4955 msgid "stars range" msgstr "rang d'estrelles" #: f.meta.cc:4956 msgid "search tags" msgstr "buscar etiquetes" #: f.meta.cc:4957 msgid "search text" msgstr "buscar text" #: f.meta.cc:4958 msgid "search files" msgstr "buscar arxius" #: f.meta.cc:4963 msgid "(yyyymmdd)" msgstr "(aaaammdd)" #: f.meta.cc:4968 msgid "last version only" msgstr "només última versió" #: f.meta.cc:4970 msgid "all/any" msgstr "tot/qualsevol" #: f.meta.cc:4989 msgid "other criteria" msgstr "altres criteris" #: f.meta.cc:4993 msgid "other" msgstr "altre" #: f.meta.cc:4999 msgid "Enter Search Tag" msgstr "Introduïr etiquetes de cerca" #: f.meta.cc:5294 #, c-format msgid "not a defined tag: %s" msgstr "no és una etiqueta definida: %s" #: f.meta.cc:5325 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:5332 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:5376 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "cerca de dades no raonable \n" " %s %s" #: f.meta.cc:5394 msgid "stars range not reasonable" msgstr "rang d'estrellas no raonable" #: f.meta.cc:5639 #, c-format msgid "images added: %d removed: %d new count: %d" msgstr "imatges afegides: %d eliminades: %d nova quantitat: %d" #: f.meta.cc:5642 msgid "no changes made" msgstr "no s'han fet canvis" #: f.meta.cc:5780 msgid "Add Geotags Search Criteria" msgstr "Afegir criteris de cerca de geoetiquetes" #: f.meta.cc:5799 msgid "range (km)" msgstr "rang (km)" #: f.meta.cc:5892 msgid "error in latitude/longitude/range" msgstr "error em latitud/longitud/rang" #: f.meta.cc:5956 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:5980 msgid "Additional Items for Report" msgstr "Detalls addicionals per a l'informe" #: f.meta.cc:5986 msgid "Keyword" msgstr "Paraula clau" #: f.meta.cc:5993 msgid "Match Criteria" msgstr "Criteri de coincidència" #: f.meta.cc:6553 msgid "image index is missing" msgstr "Necessari índex d'imatges\t" #: f.repair.cc:1041 msgid "Apply repeatedly while watching the image." msgstr "Aplicar repetidament mentre es visualitza la imatge" #: f.repair.cc:1042 msgid "Measure" msgstr "Mida" #: f.repair.cc:1078 msgid "Noise Reduction" msgstr "Reducció de soroll" #: f.repair.cc:1099 msgid "Flatten 1" msgstr "Aplanar 1" #: f.repair.cc:1100 msgid "Flatten 2" msgstr "Aplanar 2" #: f.repair.cc:1101 msgid "Median" msgstr "Mediana" #: f.repair.cc:1121 msgid "dark areas" msgstr "àrees fosques" #: f.repair.cc:1123 msgid "all areas" msgstr "totes les àrees" #: f.repair.cc:1246 msgid "Measure Noise" msgstr "Mida de soroll" #: f.repair.cc:1247 msgid "Click on a monotone image area." msgstr "Clic en una àrea de la imatge monotonal" #: f.repair.cc:2177 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:2199 f.widgets.cc:457 msgid "Smart Erase" msgstr "Esborrat intel.ligent" #: f.repair.cc:2204 fotoxx.h:1152 msgid "Radius" msgstr "Radi" #: f.repair.cc:2206 f.widgets.cc:455 msgid "Blur" msgstr "Desenfocar" #: f.repair.cc:2209 msgid "New Area" msgstr "Nova àrea" #: f.repair.cc:2558 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:2574 msgid "Red Eye Reduction" msgstr "Reducció d'ulls vermells" #: f.repair.cc:3038 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" #: f.repair.cc:3070 f.widgets.cc:459 msgid "Paint/Clone" msgstr "Pintar/Clonar" #: f.repair.cc:3074 msgid "paint color" msgstr "pintar color" #: f.repair.cc:3077 msgid "copy from image" msgstr "copiar des de imatge" #: f.repair.cc:3084 f.repair.cc:3641 msgid "paintbrush radius" msgstr "radi del pincell" #: f.repair.cc:3085 msgid "transparency center" msgstr "transparència del centre" #: f.repair.cc:3086 msgid "transparency edge" msgstr "transparència de les vores" #: f.repair.cc:3093 f.repair.cc:3648 msgid "gradual paint" msgstr "pintat gradual" #: f.repair.cc:3100 #, c-format msgid "Undo Memory %d%c" msgstr "Memmòria de desfer %d%c" #: f.repair.cc:3436 msgid "" "Undo memory limit has been reached. \n" "Save work with [done], then resume painting." msgstr "" "S'ha arribat al límit de la memòria de desfer. \n" "Guardar el treball amb [Fet], i continuar pintant." #: f.repair.cc:3601 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "arrossegar esquerra: afegir transparència \n" "arrossegar dret: afegir opacitat" #: f.repair.cc:3633 f.widgets.cc:460 msgid "Paint Transparency" msgstr "Pintar transparència" #: f.repair.cc:3642 msgid "strength center" msgstr "força en el centre" #: f.repair.cc:3643 msgid "strength edge" msgstr "força en els extrems" #: f.repair.cc:3858 f.widgets.cc:461 msgid "Color Mode" msgstr "Mode de color" #: f.repair.cc:3862 msgid "black/white positive" msgstr "positiu blanc i negre" #: f.repair.cc:3863 msgid "black/white negative" msgstr "negatiu blanc i negre" #: f.repair.cc:3864 msgid "color positive" msgstr "positiu color" #: f.repair.cc:3865 msgid "color negative" msgstr "negatiu color" #: f.repair.cc:3866 msgid "sepia" msgstr "sèpia" #: f.repair.cc:4046 f.widgets.cc:462 msgid "Shift Colors" msgstr "Modificar colors" #: f.repair.cc:4317 msgid "+Brightness" msgstr "Brillantor" #: f.repair.cc:4318 msgid "+Red -Cyan" msgstr "+Vermell -Cian" #: f.repair.cc:4319 msgid "+Green -Magenta" msgstr "+Verd -Magenta" #: f.repair.cc:4320 msgid "+Blue -Yellow" msgstr "+Blae -Groc" #: f.repair.cc:4326 fotoxx.h:1154 msgid "Red" msgstr "Vermell" #: f.repair.cc:4327 fotoxx.h:1120 msgid "Green" msgstr "Verd" #: f.repair.cc:4328 fotoxx.h:1089 msgid "Blue" msgstr "Blau" #: f.repair.cc:4623 msgid "Color to change" msgstr "Color a canviar" #: f.repair.cc:4625 msgid "shift+click on image to select color" msgstr "Majúss+clic en la imatge per a sel·leccionar color" #: f.repair.cc:4638 msgid "Color Hue" msgstr "To de color" #: f.repair.cc:4639 msgid "Saturation" msgstr "SAturació" #: f.repair.cc:4640 msgid "Lightness" msgstr "Brillantor" #: f.repair.cc:4641 msgid "Color Match" msgstr "Coincidència de color" #: f.repair.cc:4642 msgid "Adjustment" msgstr "Ajustament" #: f.repair.cc:5028 msgid "Ramp brightness across image" msgstr "Rampa de brillantor a través de la imatge" #: f.repair.cc:5352 f.tools.cc:2048 msgid "Click image to select pixels." msgstr "Clic en la imatge per seleccionar píxels." #: f.repair.cc:5383 f.widgets.cc:466 msgid "Color Ramp" msgstr "Rampa de color" #: f.repair.cc:5394 msgid "Metric:" msgstr "Mètrica:" #: f.repair.cc:5817 msgid "Color Match Images" msgstr "Concordar color d'imatges" #: f.repair.cc:5843 msgid "mouse radius for color sample" msgstr "radi del ratolí per mostra de color" #: f.repair.cc:5846 msgid "image for source color" msgstr "imatge per el color d'origen" #: f.repair.cc:5848 msgid "click on image to get source color" msgstr "clic en la imatge per obtenir color d'origen" #: f.repair.cc:5851 msgid "image to set matching color" msgstr "imatge per establir concordància de color" #: f.repair.cc:5853 msgid "click on image to set matching color" msgstr "click on image to set matching color" #: f.repair.cc:5914 msgid "select source image color first" msgstr "seleccionar primer el color de la imatge d'origen" #: f.repair.cc:6099 msgid "Change Color Profile" msgstr "Canviar perfil de color" #: f.repair.cc:6103 msgid "input profile" msgstr "perfil d'entrada" #: f.repair.cc:6107 msgid "output profile" msgstr "perfil de sortida" #: f.repair.cc:6123 msgid "color profile" msgstr "perfil de color" #: f.repair.cc:6169 f.repair.cc:6175 #, c-format msgid "unknown cms profile %s" msgstr "perfil cms desconegut %s" #: f.repair.cc:6307 f.widgets.cc:469 msgid "Remove Dust" msgstr "Retirar pols" #: f.repair.cc:6311 msgid "spot size limit" msgstr "límit de mida del punt" #: f.repair.cc:6314 msgid "max. brightness" msgstr "max. brillantor" #: f.repair.cc:6317 msgid "min. contrast" msgstr "min. contrast" #: f.repair.cc:7098 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:7121 f.widgets.cc:471 msgid "Color Fringes" msgstr "Franges de color" #: f.repair.cc:7320 f.widgets.cc:472 msgid "Stuck Pixels" msgstr "Píxels retinguts" #: f.repair.cc:7326 msgid "pixel group" msgstr "agrupar píxels" #: f.repair.cc:7334 f.repair.cc:7402 msgid "stuck pixels:" msgstr "píxels defectuosos" #: f.repair.cc:7430 msgid "Load Stuck Pixels" msgstr "Carregar píxels defectuosos" #: f.repair.cc:7432 msgid "File:" msgstr "Arxiu:" #: f.repair.cc:7457 f.repair.cc:7514 msgid "Stuck Pixels file" msgstr "Arxiu de píxels defectuosos" #: f.repair.cc:7494 msgid "file format error" msgstr "error de format d'arxiu" #: f.tools.cc:70 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:72 msgid "Select to add, click on X to delete." msgstr "Sel·leccionar per afegir, cili la X per esborrar" #: f.tools.cc:73 msgid "Select directory for thumbnails." msgstr "Sel·leccionar directori per miniatures." #: f.tools.cc:74 msgid "" "Index function terminated. Fotoxx will close. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Acabada la funció d'indexat, Fotoxx es tancarà \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:106 f.widgets.cc:513 msgid "Index Image Files" msgstr "Arxiu índex d'imatges" #: f.tools.cc:250 msgid "Choose top image directories" msgstr "Seleccionar el dirctori d'imatges principal" #: f.tools.cc:251 msgid "Choose thumbnail directory" msgstr "Escollir directori de miniatures" #: f.tools.cc:324 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:330 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" "Directori invàlid: \n" " %s \n" "Si us plau esborrar." #: f.tools.cc:333 #, 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:336 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" "Directori duplicat: \n" " %s \n" "Si us plau esborrar." #: f.tools.cc:385 msgid "no thumbnails directory defined" msgstr "directori de miniatures no definit" #: f.tools.cc:735 msgid "COMPLETED" msgstr "COMPLETAT" #: f.tools.cc:776 msgid "Indexing is required for first-time startup." msgstr "L'indexat és necessari per una primera arrencada" #: f.tools.cc:838 msgid "Recent Files Gallery" msgstr "Galeria d'arxius recents" #: f.tools.cc:839 msgid "Newest Files Gallery" msgstr "Galeria d'arxius nous" #: f.tools.cc:840 msgid "Previous Gallery" msgstr "Galeria prèvia" #: f.tools.cc:841 msgid "Previous Image" msgstr "Imatge prèvia" #: f.tools.cc:842 msgid "Blank Window" msgstr "Finestra buida" #: f.tools.cc:843 msgid "Directory" msgstr "Directori" #: f.tools.cc:844 msgid "Image File" msgstr "Arxiu d'imatges" #: f.tools.cc:887 f.widgets.cc:514 msgid "User Options" msgstr "Opcions d'usuari" #: f.tools.cc:890 msgid "Startup Display" msgstr "Pantalla d'inici" #: f.tools.cc:901 msgid "Menu Style" msgstr "Estil de menú" #: f.tools.cc:902 msgid "Icons" msgstr "Icones" #: f.tools.cc:903 msgid "Icons + Text" msgstr "Icones + text" #: f.tools.cc:905 msgid "Icon size" msgstr "tamany d'icona" #: f.tools.cc:909 msgid "Dialog font and size" msgstr "Diàleg font i mida" #: f.tools.cc:914 msgid "Image Pan/scroll:" msgstr "Fixar/desplaçar imatge" #: f.tools.cc:917 msgid "Zooms for 2x" msgstr "Zooms x2" #: f.tools.cc:926 msgid "JPEG save quality" msgstr "qualitat de l'arxiu JPEG" #: f.tools.cc:933 msgid "Curve node capture distance" msgstr "Dist'ancia de captura de nodus de corba" #: f.tools.cc:937 msgid "show hidden directories in gallery view" msgstr "mostrar directoris ocults en la vista de galeria" #: f.tools.cc:940 msgid "prev/next shows last file version only" msgstr "ant/seg mostra només la última versió de l'arxiu" #: f.tools.cc:943 msgid "RAW command" msgstr "Comandament RAW" #: f.tools.cc:947 msgid "RAW file types" msgstr "Tipus d'arxius RAW" #: f.tools.cc:1029 msgid "Delete present thumbnails to make effective" msgstr "Esborrar miniatura actual per fer-ho efectiu" #: f.tools.cc:1051 msgid "Select startup directory" msgstr "Seleccionar directori d'inici" #: f.tools.cc:1058 msgid "Select startup image file" msgstr "Seleccionarimatge d'inici" #: f.tools.cc:1096 msgid "startup directory is invalid" msgstr "directori d'inici no és vàlid" #: f.tools.cc:1107 msgid "startup file is invalid" msgstr "arxiu d'inici no és vàlid" #: f.tools.cc:1247 msgid "Edit KB Shortcuts" msgstr "Editar tecles drecera" #: f.tools.cc:1252 msgid "shortcut key:" msgstr "tecla drecera" #: f.tools.cc:1253 msgid "(enter key)" msgstr "(entra tecla)" #: f.tools.cc:1379 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Reservada, no es pot utilitzar" #: f.tools.cc:1533 msgid "unable to save KB-shortcuts file" msgstr "no es pot guardar arxiu de tecles drecera" #: f.tools.cc:1837 f.widgets.cc:517 msgid "Grid Lines" msgstr "Línies de graella" #: f.tools.cc:1846 msgid "x-spacing" msgstr "espaiat X" #: f.tools.cc:1847 msgid "x-count" msgstr "compte X" #: f.tools.cc:1848 msgid "x-enable" msgstr "habilitar X" #: f.tools.cc:1854 msgid "y-spacing" msgstr "espaiat Y" #: f.tools.cc:1855 msgid "y-count" msgstr "compta Y" #: f.tools.cc:1856 msgid "y-enable" msgstr "habilitar Y" #: f.tools.cc:1976 f.tools.cc:1981 f.widgets.cc:518 msgid "Line Color" msgstr "Color de línees" #: f.tools.cc:1978 msgid "Area Color" msgstr "Color de l'àrea" #: f.tools.cc:2083 f.widgets.cc:519 msgid "Show RGB" msgstr "Mostrar RGB" #: f.tools.cc:2375 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key M to toggle dialog." msgstr "" "Arrossegar ratolí sobre la imatge. \n" "clic esquerra per cancel.lar. \n" "Tecla M per amagar diàleg." #: f.tools.cc:2401 f.widgets.cc:520 msgid "Magnify Image" msgstr "Augmentar imaten" #: f.tools.cc:2410 msgid "X-size" msgstr "Lupa" #: f.tools.cc:2655 msgid "Darkest and Brightest Pixels" msgstr "Píxels més foscos i més brillants" #: f.tools.cc:2678 msgid "Dark Limit" msgstr "Ombra" #: f.tools.cc:2679 msgid "Bright Limit" msgstr "Llum" #: f.tools.cc:2774 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:2870 f.widgets.cc:523 msgid "Monitor Gamma" msgstr "Gamma del monitor" #: f.tools.cc:2930 msgid "Available Translations" msgstr "Traduccions disponibles" #: f.tools.cc:2934 msgid "Set Language" msgstr "Sel.leccionar idioma" #: f.tools.cc:3071 f.widgets.cc:526 msgid "Calibrate Printer" msgstr "Calibrar impresora" #: f.tools.cc:3096 msgid "print color chart" msgstr "imprimir carta de colors" #: f.tools.cc:3097 msgid "scan and save color chart" msgstr "escanejar y desar carta de colors" #: f.tools.cc:3098 msgid "align and trim color chart" msgstr "adressar i retalla carta de colors" #: f.tools.cc:3099 msgid "open and process color chart" msgstr "obrir i processar carta de colors" #: f.tools.cc:3100 msgid "print image with revised colors" msgstr "imprimir imatge amb colors revisats" #: f.tools.cc:3264 #, c-format msgid "" "Scan the printed color chart. \n" "Save in %s/" msgstr "" "Escanejar la carta de colors impresa. \n" "Desar en %s/" #: f.tools.cc:3278 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation caused by the scanner. \n" "Trim off all margins. Be careful not to cut off \n" "any color tile edges or leave any white margins." msgstr "" "Obrir i editar la carta de colors escanejada. \n" "Eliminar qualsevol obliqüitat o rotació causada per l'escàner. \n" "Retallar tots els marges. Alerta amb no tallar \n" "qualsevol zona de color o deixar qualsevol marge." #: f.tools.cc:3310 msgid "Open the trimmed color chart file" msgstr "Obrir la carta de colors retallada" #: f.tools.cc:3443 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:3483 msgid "Color map file to use" msgstr "Arxiu de mapa de color per utilitzar" #: f.tools.cc:3500 msgid "Select the image file to print." msgstr "Sel·leccionar imatge a imprimir" #: f.tools.cc:3565 msgid "converting colors..." msgstr "convertint colors" #: f.tools.cc:3665 msgid "Image colors are converted for printing." msgstr "Colors de la imatge convertits per imprimir" #: f.widgets.cc:111 msgid "Album" msgstr "Àlbum" #: f.widgets.cc:113 msgid "TOP" msgstr "DALT" #: f.widgets.cc:165 msgid "Current Image File (key F)" msgstr "Arxiu d'imatge actual (tecla F)" #: f.widgets.cc:166 msgid "Thumbnail Gallery (key G)" msgstr "Galería de miniatures (tecla G)" #: f.widgets.cc:167 msgid "World Maps (key W)" msgstr "Mapa mundial (tecla W)" #: f.widgets.cc:170 msgid "Favorite Functions" msgstr "Funcions favorites" #: f.widgets.cc:171 msgid "File: Open, RAW, Rename, Trash, Print" msgstr "Arxiu: Obrir, RAW, Re-anoimenar, Paperera, Imprimir" #: f.widgets.cc:172 msgid "Save modified image file to disk" msgstr "Guardar imatge modificada en el disc" #: f.widgets.cc:173 msgid "Open previous or next file (left/right mouse click)" msgstr "Obrir arxiu anterior o sugüent (clic esq/dret)" #: f.widgets.cc:174 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "Metadades: Titols, Etiquetes, Valoracions, Geoetiquetes, Buscar ..." #: f.widgets.cc:175 msgid "Areas: Select areas to edit, copy and paste" msgstr "Arees: Sel.leccionar àrees per editar, copiar i enganxar" #: f.widgets.cc:176 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "Editar: Retallar, Girar, Redimensionar, Brillantor, Contrast, Text ..." #: f.widgets.cc:177 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "Reparar: Enfocar, Soroll, Ulls vermells, Color, Pintar, Clonar ..." #: f.widgets.cc:178 msgid "Bend: Fix Perspective, Bend/Warp image ..." msgstr "Deformar: Corretgir perspectiva, Deformar/Corretgir imatge" #: f.widgets.cc:179 msgid "Effects: Special Effects, Arty Transforms" msgstr "Efectes: Efectes especials, Transformacions artístiques" #: f.widgets.cc:180 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "Combinar: HDR, HDF, Panorama, Apilar, Muntatge" #: f.widgets.cc:181 msgid "" "Undo or Redo one edit (left/right mouse click) \n" " hold key A to include all edits" msgstr "" "Desfer o refer una edició (clic esq/dret) \n" " prèmer tecla A per incluir totes les edicions" #: f.widgets.cc:183 msgid "Tools: Index, Options, Shortcuts, Magnify ..." msgstr "Eines: Indexar, Opcions, Dresseres, Augmentar ..." #: f.widgets.cc:184 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "Ajuda: Inici ràpid, Guia d'usuari, Canvis recents" #: f.widgets.cc:187 msgid "Set gallery from current image file" msgstr "Establir galeria des de l'imatge actual" #: f.widgets.cc:188 msgid "Albums: Manage Albums, Slide Show, Desktop Wallpaper" msgstr "Âlbums: Administrar àlbums, Diaporama, Fons de pantalla" #: f.widgets.cc:189 msgid "go to bookmarked image" msgstr "anar a imatge marcada" #: f.widgets.cc:190 msgid "increase thumbnail size" msgstr "Augmentar mida de miniatura" #: f.widgets.cc:191 msgid "reduce thumbnail size" msgstr "reduir mida de miniatura" #: f.widgets.cc:192 msgid "change sort order" msgstr "canviar criteri d'ordenació" #: f.widgets.cc:193 msgid "jump to beginning (top)" msgstr "anar al principi (a dalt)" #: f.widgets.cc:194 msgid "jump to end (bottom)" msgstr "anar al final (a baix)" #: f.widgets.cc:195 msgid "previous page" msgstr "pàgina anterior" #: f.widgets.cc:196 msgid "next page" msgstr "pàgina següent" #: f.widgets.cc:197 msgid "slow scroll" msgstr "desplaçament lent" #: f.widgets.cc:198 msgid "Batch conversion, metadata, RAW processing" msgstr "Conversió en lot, metadades, processat RAW" #: f.widgets.cc:201 msgid "Choose a map for image locations" msgstr "Escollir mapa per ubicar imatge" #: f.widgets.cc:202 msgid "Set image search radius for map click" msgstr "Ajustar radi de cerca d'imatge per clicar en el mapa" #: f.widgets.cc:205 msgid "Open another window" msgstr "Obrir una altre finestra" #: f.widgets.cc:206 msgid "Open a new image file" msgstr "Obrir una nova imatge" #: f.widgets.cc:207 msgid "Open the previously seen file" msgstr "Obrir arxiu vist previament" #: f.widgets.cc:208 msgid "Open a recently seen file" msgstr "Obrir arxiu vist recentment" #: f.widgets.cc:209 msgid "Open a newly added file" msgstr "Obrir un nou arxiu afegit" #: f.widgets.cc:210 f.widgets.cc:211 msgid "Open and edit a camera RAW file" msgstr "Obrir i editar un arxiu de càmera RAW" #: f.widgets.cc:212 msgid "Change the image file name" msgstr "Canvia nom de la imatge" #: f.widgets.cc:213 msgid "Create a blank image" msgstr "Crear una imatge buida" #: f.widgets.cc:214 msgid "Move image file to Trash" msgstr "Moure imatge a la paperera" #: f.widgets.cc:215 msgid "Permanently delete image file" msgstr "Esborrar imatge permanentment" #: f.widgets.cc:216 msgid "Print the current image" msgstr "Impprimir la imatge actual" #: f.widgets.cc:217 msgid "Print current image with adjusted colors" msgstr "Imprimir imatge actual amb els colors ajustats" #: f.widgets.cc:218 f.widgets.cc:406 msgid "Quit Fotoxx" msgstr "Sortir de Fotoxx" #: f.widgets.cc:221 msgid "List a few key metadata items" msgstr "Llistar algunes dades clau de metadades" #: f.widgets.cc:222 msgid "List all metadata items" msgstr "Listar totes les metadades" #: f.widgets.cc:223 msgid "(Toggle) show captions and comments" msgstr "(Canviar) mostra llegendes i comentaris" #: f.widgets.cc:224 msgid "Edit image tags/caption/rating ..." msgstr "Editar etiquetes/llegenda/clasificació de la imatge" #: f.widgets.cc:225 msgid "Edit any image metadata" msgstr "Editar qualsevol metadada de la imatge" #: f.widgets.cc:226 msgid "Remove all metadata from an image" msgstr "Eliminar totes les metadades d'una imatge" #: f.widgets.cc:227 msgid "Add/remove tags for multiple images" msgstr "Afegir/eliminar etiquetes per múltiples imatges " #: f.widgets.cc:228 msgid "Convert tag names for all images" msgstr "Convertir noms d'etiqueta per totes les imatges" #: f.widgets.cc:229 msgid "Add/change/delete metadata for multiple images" msgstr "Afegir/canviar/esborrar metadades a múltiples imatges" #: f.widgets.cc:230 msgid "Edit image location and geotags" msgstr "Editar ubicació i geoetiquetes de la imatge" #: f.widgets.cc:231 msgid "Add/revise geotags for multiple images" msgstr "Afegir/revisar geo-etiquetes a múltiples imatges" #: f.widgets.cc:232 msgid "Find all images for a location [date]" msgstr "Trobar totes les imatges per una ubicació [data]" #: f.widgets.cc:233 msgid "Find images meeting select criteria" msgstr "Trobar imatges que compleixin un criteri de seleccio" #: f.widgets.cc:236 msgid "Select object or area for editing" msgstr "Seleccionar objecte o àrea per editar" #: f.widgets.cc:237 msgid "Show (outline) existing area" msgstr "Mostrar un àrea existent (contorn)" #: f.widgets.cc:238 msgid "Hide existing area" msgstr "Ocultar àrea existent" #: f.widgets.cc:239 msgid "Enable area for editing" msgstr "Activar àrea per editar" #: f.widgets.cc:240 msgid "Disable area for editing" msgstr "Desactivat àrea per editar" #: f.widgets.cc:241 msgid "Reverse existing area" msgstr "Invertir àrea existent" #: f.widgets.cc:242 msgid "Erase existing area" msgstr "Esborrar àrea existent" #: f.widgets.cc:243 msgid "Copy area for later pasting into image" msgstr "Copiar àrea per posterior enganxat en una imatge" #: f.widgets.cc:244 msgid "Paste previously copied area into image" msgstr "Enganxar en la imatge àrea previament copiada" #: f.widgets.cc:245 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:246 msgid "Save area to a file with transparency" msgstr "Guardar àrea en un arxiu amb tranparència" #: f.widgets.cc:249 msgid "Trim/Crop margins and/or Rotate" msgstr "Retallar/tallar marges i/o girar" #: f.widgets.cc:250 msgid "Upright a rotated image" msgstr "Adressar una imatge girada" #: f.widgets.cc:251 f.widgets.cc:252 msgid "Fast auto enhance that may work OK" msgstr "Auto-millora ràpida" #: f.widgets.cc:253 msgid "Adjust brightness, contrast, color" msgstr "Ajustar brillantor, contrast, color" #: f.widgets.cc:254 msgid "Add local contrast, enhance details" msgstr "Afegir contrast local, millorar detalls" #: f.widgets.cc:255 msgid "Adjust brightness distribution" msgstr "Ajustar distribució de la brillantor" #: f.widgets.cc:256 msgid "Flatten zonal brightness distribution" msgstr "Aplanar zones de l'histograma" #: f.widgets.cc:257 msgid "Change pixel dimensions" msgstr "Canviar dimensions de píxels" #: f.widgets.cc:258 msgid "Mirror image horizontally or vertically" msgstr "Enmirallar la imatge horizontalment o verticalment" #: f.widgets.cc:259 msgid "Write text on image" msgstr "Escriure un text en la imatge" #: f.widgets.cc:260 msgid "Write lines or arrows on image" msgstr "Dibuixar línees o fletxes en la imatge" #: f.widgets.cc:261 msgid "Fix brightness uniformity across image" msgstr "Corretgir la uniformitat de la brillantor per zones de la imatge" #: f.widgets.cc:262 msgid "Paint edit function gradually with mouse" msgstr "Funció d'edició pintant gradualment amb el ratolí" #: f.widgets.cc:263 msgid "Leverage edits by brightness or color" msgstr "Edició per nivells de brillantor o color" #: f.widgets.cc:264 msgid "Edit plugins menu or run a plugin function" msgstr "Editar menú de plugins o executar una funció de plugin" #: f.widgets.cc:267 msgid "Make the image look sharper" msgstr "Fer que la imatge es vegi més enfocada" #: f.widgets.cc:268 msgid "Make the image look fuzzy" msgstr "Fer que la imatge es vegi desenfocada" #: f.widgets.cc:269 msgid "Filter noise from low-light photos" msgstr "Filtrar soroll de fotografíes fetes amb poca llum" #: f.widgets.cc:270 msgid "Remove unwanted objects" msgstr "Eliminar objectes no desitjats" #: f.widgets.cc:271 msgid "Fix red-eyes from electronic flash" msgstr "Corretgir ulls vermells produïts per un flash electrònic" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Pintar utilitzant el ratolí" #: f.widgets.cc:273 msgid "Paint image transparency using the mouse" msgstr "Pintar transparència d'imatge amb el ratolí" #: f.widgets.cc:274 msgid "Make BW/color, negative/positive, sepia" msgstr "Blanc i negre/color, negatiu/positiu, sèpia" #: f.widgets.cc:275 msgid "Shift/convert colors into other colors" msgstr "Canviar/convertir uns colors en altres" #: f.widgets.cc:276 msgid "Adjust color using RGB or CMY colors" msgstr "Ajustar coilor usant sistema RGB o CMY" #: f.widgets.cc:277 msgid "Adjust color using HSL colors" msgstr "Ajustar color usant sistema HSL" #: f.widgets.cc:278 msgid "Adjust color in selected image areas" msgstr "Ajustar color en una àrea seleccionada de una imatge" #: f.widgets.cc:279 msgid "Match colors on one image with another" msgstr "Fer concordar colors d'una imatge amb els d'altra" #: f.widgets.cc:280 msgid "Convert to another color profile" msgstr "Convertir a un perfil de color" #: f.widgets.cc:281 msgid "Remove dust spots from scanned slides" msgstr "Eliminar pols de diapositives escanejades" #: f.widgets.cc:282 msgid "Smoothen edges with jaggies" msgstr "Suavitzar vores amb dents de serr" #: f.widgets.cc:283 msgid "Reduce Chromatic Abberation" msgstr "Reduïr l'aberració cromàtica" #: f.widgets.cc:284 msgid "Erase known hot and dark pixels" msgstr "Esborrar píxels calents i foscos coneguts" #: f.widgets.cc:287 msgid "Remove curvature, esp. panoramas" msgstr "Eliminar corbatura, especialment d'un panorama" #: f.widgets.cc:288 msgid "Straighten objects seen from an angle" msgstr "Redreçar objectes vistos des d'un angle" #: f.widgets.cc:289 msgid "Distort image areas using the mouse" msgstr "Distorsionar una àrea de una imatge utilitzant el ratolí" #: f.widgets.cc:290 f.widgets.cc:291 f.widgets.cc:292 msgid "Distort the whole image using the mouse" msgstr "Distorsionar tota la imatge utilitzant el ratolí" #: f.widgets.cc:293 msgid "Flatten a photographed book page" msgstr "Aplanar una pàgina impresa fotografiada" #: f.widgets.cc:296 msgid "Reduce color depth (posterize)" msgstr "Reduïr profunditat de color (posteritzar)" #: f.widgets.cc:297 msgid "Convert to pencil sketch" msgstr "Convertir en un esbós a llapis" #: f.widgets.cc:298 msgid "Convert to line drawing (edge detection)" msgstr "Convertir a un dibuix de línees (detecció de vores)" #: f.widgets.cc:299 msgid "Convert to solid color drawing" msgstr "Convertir a un dibuix de colors sòlids" #: f.widgets.cc:300 msgid "Graduated Blur depending on contrast" msgstr "Desenfocament graduat depenent del contrast" #: f.widgets.cc:301 msgid "Create an embossed or 3D appearance" msgstr "Crear un relleu o aparença 3D" #: f.widgets.cc:302 msgid "Convert to square tiles" msgstr "Convertir a rajoles quadrades" #: f.widgets.cc:303 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "Convertir a punts (efecte Roy Lichtenstein" #: f.widgets.cc:304 msgid "Convert into a simulated painting" msgstr "Convertir a una pintura simulada" #: f.widgets.cc:305 msgid "Change brightness or color radially" msgstr "Canviar brillantor o color radialment" #: f.widgets.cc:306 msgid "Add texture to an image" msgstr "Afegir textura a una imatge" #: f.widgets.cc:307 msgid "Tile image with a repeating pattern" msgstr "Enrajolñar imatge amb un patrò repetitiu" #: f.widgets.cc:308 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:309 msgid "Process an image using a custom kernel" msgstr "Processar una imatge fent servir una matriu personalitzada" #: f.widgets.cc:310 msgid "Warp an image with a wave pattern" msgstr "Deformar una imatge amb ​un patró d'ones" #: f.widgets.cc:311 msgid "Blur an image in the direction of mouse drags" msgstr "Desenfocar una imatge en la direcció del moviment del ratolí" #: f.widgets.cc:312 msgid "Make a spherical projection of an image" msgstr "Fer una projecció esfèrica de una imatge" #: f.widgets.cc:315 msgid "Combine bright/dark images for better detail" msgstr "Combinar imatges fosques/il.luminadas per millorar detalls" #: f.widgets.cc:316 msgid "Combine near/far focus images for deeper focus" msgstr "" "Combinar imatges enfocades aprop/lluny per obtenir major profunditat de camp" #: f.widgets.cc:317 msgid "Combine images to erase passing people, etc." msgstr "Combinar imatges per esborrar vianants, cotxes, etc" #: f.widgets.cc:318 msgid "Combine noisy images into a low-noise image" msgstr "Combinar imatges amb soroll en imatges amb poc soroll" #: f.widgets.cc:319 msgid "Combine images into a panorama" msgstr "Combinar imatges en un panorama" #: f.widgets.cc:320 msgid "Combine images into a vertical panorama" msgstr "Combinar imatges en un panorama vertical" #: f.widgets.cc:321 msgid "Combine images into a panorama (panorama tools)" msgstr "Combinar imatges en un panorama (panorama tools)" #: f.widgets.cc:322 msgid "Arrange images and text in a layout (montage)" msgstr "Ordenar imatges i text en una capa (muntatge)" #: f.widgets.cc:325 msgid "Index new files and make thumbnails" msgstr "Indexar nous arxius i fer miniatures" #: f.widgets.cc:326 msgid "Change user preferences" msgstr "Canviar preferències d'usuari" #: f.widgets.cc:327 msgid "Change Keyboard Shortcut Keys" msgstr "Canviar tecles drecera" #: f.widgets.cc:328 msgid "Show a brightness distribution graph" msgstr "Mostrar histograma (distribució de la brillantor)" #: f.widgets.cc:329 msgid "Show or revise grid lines" msgstr "Mostrar o amagar línies de graella" #: f.widgets.cc:330 msgid "Change color of foreground lines" msgstr "Canviar color a línies de contorn" #: f.widgets.cc:331 msgid "Show RGB colors at mouse click" msgstr "Mostrar colors RGB amb un clic de ratolí" #: f.widgets.cc:332 msgid "Magnify image around the mouse position" msgstr "Augmenta la imatge al voltant de la posició del ratolí" #: f.widgets.cc:333 msgid "Highlight darkest and brightest pixels" msgstr "Resaltar píxels més foscos o més brillants" #: f.widgets.cc:334 msgid "Chart to adjust monitor color" msgstr "Carta per ajustar el color del monitor" #: f.widgets.cc:335 msgid "Chart to adjust monitor gamma" msgstr "Carta per ajustar la gamma del monitor" #: f.widgets.cc:336 msgid "Change the GUI language" msgstr "Canviar idioma de la interfície d'usuari" #: f.widgets.cc:337 msgid "Report missing translations" msgstr "Informe de traduccions que falten" #: f.widgets.cc:338 msgid "Calibrate printer colors" msgstr "Calibrar colors d'impresora" #: f.widgets.cc:339 msgid "Memory and CPU (to terminal/logfile)" msgstr "Memoria i CPU (al terminal/arxiu de registre)" #: f.widgets.cc:343 msgid "Rename/convert/resize/move multiple files" msgstr "Reanomenar/convertir/redimensionar/moure múltiples arxius" #: f.widgets.cc:344 msgid "Upright multiple rotated image files" msgstr "Adressar múltiples imatges girades" #: f.widgets.cc:345 msgid "Delete or Trash multiple files" msgstr "Esborrar/eliminar múltiples arxius" #: f.widgets.cc:346 msgid "Convert camera RAW files using DCraw" msgstr "Convertir arxius RAW usant DCraw" #: f.widgets.cc:347 msgid "Convert camera RAW files using Raw Therapee" msgstr "Convertir arxius RAW usant RawTherapee" #: f.widgets.cc:348 msgid "Build and run edit script files" msgstr "Escriure y executar scripts editats" #: f.widgets.cc:349 msgid "Burn selected image files to CD or DVD" msgstr "Gravar imatges seleccionades en un CD/DVD" #: f.widgets.cc:350 msgid "Search all image files and report duplicates" msgstr "Buscar totes les imatges i informar dels duplicats" #: f.widgets.cc:354 msgid "Quick Start mini-guide" msgstr "Minni guia d'inici ràpid" #: f.widgets.cc:355 msgid "Read the user guide" msgstr "Llegir la guia d'usuari" #: f.widgets.cc:356 msgid "Recent user guide changes" msgstr "Canvis recents a la guia d'usuari" #: f.widgets.cc:357 msgid "Summary of image edit functions" msgstr "Resum de les funcions d'edició d'imatge" #: f.widgets.cc:358 msgid "Technical installation notes" msgstr "Notes tècniques d'instal.lació " #: f.widgets.cc:359 msgid "List updates by Fotoxx version" msgstr "Llistar actualitzacions per versió de Fotoxx" #: f.widgets.cc:360 msgid "View the log file and error messages" msgstr "Veure l'arxiu de registre i missatges d'error" #: f.widgets.cc:361 msgid "How to do Fotoxx translations" msgstr "Com fer traduccións de Fotoxx" #: f.widgets.cc:362 msgid "Show the Fotoxx web page" msgstr "Mostrar la pàgina web de Fotoxx" #: f.widgets.cc:363 msgid "Version, license, contact, credits" msgstr "Versió, llicència, contacte, crèdits" #: f.widgets.cc:366 msgid "Organize images into albums" msgstr "Organitzar imatges en àlbums" #: f.widgets.cc:367 msgid "Start a slide show" msgstr "Iniciar un diaporama" #: f.widgets.cc:368 msgid "Set desktop wallpaper from current Fotoxx image" msgstr "Establir fons d'escriptori des de la imatge de Fotoxx actual" #: f.widgets.cc:369 msgid "Cycle desktop wallpaper from a Fotoxx album" msgstr "Fons d'escritori cíclic des d'un àlbum de Fotoxx" #: f.widgets.cc:391 msgid "New Window" msgstr "Nova finestra" #: f.widgets.cc:392 msgid "Sync Gallery" msgstr "Sincronitzar galería" #: f.widgets.cc:393 msgid "Recently Seen Images" msgstr "Imatges vistes recentment " #: f.widgets.cc:394 msgid "Newest Images" msgstr "Imatges més recents" #: f.widgets.cc:396 msgid "Open Previous File" msgstr "Obrir arxiu anterior" #: f.widgets.cc:397 msgid "Open RAW (ufraw)" msgstr "Obrir RAW (ufraw)" #: f.widgets.cc:398 msgid "Open RAW (Raw Therapee)" msgstr "Obrir RAW (Raw Therapee)" #: f.widgets.cc:399 msgid "New Blank Image" msgstr "Nova imatge buida" #: f.widgets.cc:402 msgid "Delete Image File" msgstr "Esborrar imatge" #: f.widgets.cc:403 msgid "Print Image" msgstr "Imprimir imatge" #: f.widgets.cc:404 msgid "Print Calibrated Image" msgstr "Imprimir imatge calibrada" #: f.widgets.cc:405 f.widgets.cc:550 msgid "Set Desktop Wallpaper" msgstr "Establir fons d'escriptori" #: f.widgets.cc:409 msgid "View Metadata (short)" msgstr "Veure metadades (curt)" #: f.widgets.cc:410 msgid "View Metadata (long)" msgstr "Veure metadades (llarg)" #: f.widgets.cc:411 msgid "Show Captions on Image" msgstr "Mostrar llegendes a la imatge" #: f.widgets.cc:413 msgid "Edit Any Metadata" msgstr "Editar qualsevol metadada" #: f.widgets.cc:416 f.widgets.cc:541 msgid "Batch Rename Tags" msgstr "Reanomenar etiquetes en lot" #: f.widgets.cc:417 f.widgets.cc:542 msgid "Batch Add/Change Metadata" msgstr "Afegir/canviar metadades en lot" #: f.widgets.cc:420 f.widgets.cc:544 f.widgets.cc:618 msgid "Images by Geotag" msgstr "Imatges per Geotag" #: f.widgets.cc:421 f.widgets.cc:545 f.widgets.cc:619 msgid "Search Images" msgstr "Buscar imatges" #: f.widgets.cc:424 fotoxx.h:1168 msgid "Select" msgstr "Seleccionar" #: f.widgets.cc:425 fotoxx.h:1170 msgid "Show" msgstr "Mostrar" #: f.widgets.cc:426 fotoxx.h:1124 msgid "Hide" msgstr "Ocultar" #: f.widgets.cc:427 fotoxx.h:1110 msgid "Enable" msgstr "Activar" #: f.widgets.cc:428 fotoxx.h:1106 msgid "Disable" msgstr "Desactivar" #: f.widgets.cc:429 fotoxx.h:1127 msgid "Invert" msgstr "Invertir" #: f.widgets.cc:430 fotoxx.h:1184 msgid "Unselect" msgstr "Deseleccionar" #: f.widgets.cc:431 msgid "Copy Area" msgstr "Copiar àrea" #: f.widgets.cc:432 msgid "Paste Area" msgstr "Enganxar àrea" #: f.widgets.cc:433 msgid "Open Area File" msgstr "Obrir arxiu d'àrea" #: f.widgets.cc:434 msgid "Save Area File" msgstr "Desar arxiu d'àrea" #: f.widgets.cc:439 msgid "Voodoo 1" msgstr "Auto-millora 1" #: f.widgets.cc:440 msgid "Voodoo 2" msgstr "Auto-millora 2" #: f.widgets.cc:442 f.widgets.cc:687 msgid "Brightness Dist." msgstr "Dist. de la brillantor" #: f.widgets.cc:443 f.widgets.cc:688 msgid "Zonal Flatten" msgstr "Aplanat zonañl" #: f.widgets.cc:447 msgid "Add Text" msgstr "Afegir text" #: f.widgets.cc:448 msgid "Add Lines" msgstr "Afegir línees" #: f.widgets.cc:451 msgid "Plugins" msgstr "Plugins" #: f.widgets.cc:456 msgid "Denoise" msgstr "Reduïr soroll" #: f.widgets.cc:458 msgid "Red Eyes" msgstr "Ulls vermells" #: f.widgets.cc:463 msgid "Adjust RGB/CMY" msgstr "Ajustar RGB7CMY" #: f.widgets.cc:464 msgid "Adjust HSL" msgstr "Ajustar HSL" #: f.widgets.cc:465 msgid "Brightness Ramp" msgstr "Rampa de brillantor" #: f.widgets.cc:467 msgid "Match Colors" msgstr "Concordar colors" #: f.widgets.cc:468 msgid "Color Profile" msgstr "Perfil de color" #: f.widgets.cc:470 msgid "Anti-Alias" msgstr "Suavitzar contorns" #: f.widgets.cc:476 msgid "Fix Perspective" msgstr "Corregir perspectiva" #: f.widgets.cc:481 msgid "Flatten Book Page" msgstr "Aplanar una pàgina impresa" #: f.widgets.cc:484 msgid "Color Depth" msgstr "Profunditat de color" #: f.widgets.cc:485 msgid "Sketch" msgstr " Esbós" #: f.widgets.cc:489 msgid "Embossing" msgstr "Relleu" #: f.widgets.cc:491 msgid "Dots" msgstr "Punts" #: f.widgets.cc:492 msgid "Painting" msgstr "Pintura" #: f.widgets.cc:494 msgid "Texture" msgstr "Textura" #: f.widgets.cc:496 msgid "Mosaic" msgstr "Mosaïc" #: f.widgets.cc:503 msgid "High Dynamic Range" msgstr "HDR alt rang dinàmic" #: f.widgets.cc:504 msgid "High Depth of Field" msgstr "HDF alta profunditat de camp" #: f.widgets.cc:505 msgid "Stack/Paint" msgstr "Apilar/Pintar" #: f.widgets.cc:506 msgid "Stack/Noise" msgstr "Apilar/Soroll" #: f.widgets.cc:507 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:508 msgid "Vertical Panorama" msgstr "Panorama vertical" #: f.widgets.cc:509 msgid "PT Panorama" msgstr "PT panorama" #: f.widgets.cc:510 msgid "Mashup" msgstr "Muntatge" #: f.widgets.cc:515 msgid "Keyboard Shortcuts" msgstr "Tecles drecera" #: f.widgets.cc:516 msgid "Show Brightness Dist." msgstr "Mostrar dist. de brillantor" #: f.widgets.cc:521 msgid "Dark/Bright Pixels" msgstr "Sobre/subexposició" #: f.widgets.cc:522 msgid "Monitor Color" msgstr "Color del monitor" #: f.widgets.cc:524 msgid "Change Language" msgstr "Canviar idioma" #: f.widgets.cc:525 msgid "Missing Translations" msgstr "Traduccions que falten" #: f.widgets.cc:527 msgid "Resources" msgstr "Recursos" #: f.widgets.cc:534 msgid "Batch RAW (DCraw)" msgstr "Convertir Raw en lot (DCraw)" #: f.widgets.cc:535 msgid "Batch RAW (Raw Therapee)" msgstr "Convertir Raw en lot (RawTherapee)" #: f.widgets.cc:536 msgid "Script Files" msgstr "Scripts" #: f.widgets.cc:537 msgid "Burn Images to CD/DVD" msgstr "Gravar imatges en un CD/DVD" #: f.widgets.cc:551 msgid "Cycle Desktop Wallpaper" msgstr "Fons d'escritori cíclic" #: f.widgets.cc:568 f.widgets.cc:591 f.widgets.cc:613 msgid "Gallery" msgstr "Galeria" #: f.widgets.cc:569 f.widgets.cc:592 f.widgets.cc:614 msgid "World Maps" msgstr "Mapes mundials" #: f.widgets.cc:571 msgid "Favorites" msgstr "Favorits" #: f.widgets.cc:573 fotoxx.h:1164 msgid "Save" msgstr "Guardar" #: f.widgets.cc:574 msgid "Prev/Next" msgstr "Ant/Seg" #: f.widgets.cc:575 msgid "Metadata" msgstr "Metadades" #: f.widgets.cc:576 msgid "Areas" msgstr "Àrees" #: f.widgets.cc:577 fotoxx.h:1109 msgid "Edit" msgstr "Editar" #: f.widgets.cc:578 msgid "Repair" msgstr "Reparar" #: f.widgets.cc:579 msgid "Bend" msgstr "Deformar" #: f.widgets.cc:580 msgid "Effects" msgstr "Efectes" #: f.widgets.cc:581 msgid "Combine" msgstr "Combinar" #: f.widgets.cc:582 msgid "Undo/Redo" msgstr "Desfer/Refer" #: f.widgets.cc:583 msgid "Tools" msgstr "Eines" #: f.widgets.cc:594 msgid "Sync.G" msgstr "Sincr.G" #: f.widgets.cc:595 msgid "Albums" msgstr "Àlbums" #: f.widgets.cc:596 msgid "Bookmarks" msgstr "Marques" #: f.widgets.cc:605 msgid "Batch" msgstr "Lot" #: f.widgets.cc:616 msgid "Choose Map" msgstr "Escollir mapa" #: f.widgets.cc:617 msgid "Search Range" msgstr "Rang de cerca" #: f.widgets.cc:625 fotoxx.h:1182 msgid "Undo" msgstr "Desfer" #: f.widgets.cc:626 fotoxx.h:1155 msgid "Redo" msgstr "Refer" #: f.widgets.cc:667 msgid "Popup Image" msgstr "Imatge emergent" #: f.widgets.cc:668 msgid "Popup Image (add)" msgstr "Imatge emergent (afegir)" #: f.widgets.cc:672 fotoxx.h:1158 msgid "Rename" msgstr "Canviar el nom" #: f.widgets.cc:673 msgid "Copy to Location" msgstr "Copiar a la ubicació" #: f.widgets.cc:674 msgid "Move to Location" msgstr "Moure a la ubicació" #: f.widgets.cc:675 msgid "Copy to Clipboard" msgstr "Copiar al portapapers" #: f.widgets.cc:676 msgid "Remove from Album" msgstr "Eliminar de l'àlbum" #: f.widgets.cc:677 msgid "Cut to Image Cache" msgstr " Tallar a la cache d'imatge" #: f.widgets.cc:678 msgid "Copy to Image Cache" msgstr "Copiar a la cache d'imatge " #: f.widgets.cc:679 msgid "Paste Image Cache Here (clear)" msgstr "Enganxar aquí cache d'imatge (esborrar) " #: f.widgets.cc:680 msgid "Paste Image Cache Here (keep)" msgstr "Enganxar aquí cache d'imatge (guardar) " #: f.widgets.cc:681 msgid "Paste Current Image File Here" msgstr "Enganxar aquí la iamtge actual" #: f.widgets.cc:685 msgid "Voodoo Enhance" msgstr "Millora automàtica" #: f.widgets.cc:690 msgid "Select Area" msgstr "Select Area" #: f.widgets.cc:693 fotoxx.h:1178 msgid "Trash" msgstr "Eliminar" #: f.widgets.cc:819 msgid "Image Locations" msgstr "Ubicació d'imatge" #: fotoxx-15.11.cc:373 msgid "Please install missing programs:" msgstr "Si us plau instal.leu programas requerits:" #: fotoxx-15.11.cc:662 msgid "Kill active dialog?" msgstr "Tancar diàleg actiu?" #: fotoxx-15.11.cc:733 msgid "(reduced)" msgstr "(reduït)" #: fotoxx-15.11.cc:734 msgid "area active" msgstr "àrea activa" #: fotoxx-15.11.cc:735 msgid "dialog open" msgstr "dialog obert" #: fotoxx-15.11.cc:736 msgid "blocked" msgstr "bloquejat" #: fotoxx-15.11.cc:786 msgid "edits" msgstr "edicions" #: fotoxx-15.11.cc:2793 msgid "Exceed 50 anchor points" msgstr "Excedits punts d'anclatge" #: fotoxx-15.11.cc:3013 msgid "load curve from a file" msgstr "carregar corba des d'un arxiu" #: fotoxx-15.11.cc:3079 msgid "curve file is invalid" msgstr "Atxiu de corba no vàlid" #: fotoxx-15.11.cc:3093 msgid "save curve to a file" msgstr "guardar corba en un arxiu" #: fotoxx-15.11.cc:3165 msgid "Too many edits, please save image" msgstr "Massa edicions, si us plau guardeu la imatge" #: fotoxx-15.11.cc:3170 msgid "this function cannot be scripted" msgstr "" #: fotoxx-15.11.cc:3187 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Àrea seleccionada no activa.\n" "Continuar?" #: fotoxx-15.11.cc:3499 msgid "file data does not fit dialog" msgstr "les dades d'arxiu no s'ajusten al diàleg" #: fotoxx-15.11.cc:3509 msgid "Save settings to a file" msgstr "Desar ajustos en un arxiu" #: fotoxx-15.11.cc:3825 msgid "This action will discard changes to current image" msgstr "Aquesta acció descxartarà els canvis a la imatge actual" #: fotoxx-15.11.cc:3826 msgid "prior function still active" msgstr "funció anterior encara activa" #: fotoxx-15.11.cc:3827 fotoxx.h:1128 msgid "Keep" msgstr "Guardar" #: fotoxx-15.11.cc:3828 msgid "Discard" msgstr "Descartar" #: fotoxx.h:1080 msgid "Add" msgstr "Afegir" #: fotoxx.h:1081 msgid "Add All" msgstr "Afegir tot" #: fotoxx.h:1083 msgid "Amount" msgstr "Quantitat" #: fotoxx.h:1084 msgid "Angle" msgstr "Angle" #: fotoxx.h:1085 msgid "Apply" msgstr "Aplicar" #: fotoxx.h:1086 msgid "Auto" msgstr "Auto" #: fotoxx.h:1087 msgid "Black" msgstr "Negre" #: fotoxx.h:1088 msgid "Blend Width" msgstr "Barrejar per ample" #: fotoxx.h:1090 zfuncs.cc:10098 msgid "bottom" msgstr "a baix" #: fotoxx.h:1092 msgid "Browse" msgstr "Examinar" #: fotoxx.h:1093 msgid "Cancel" msgstr "Cancel.lar" #: fotoxx.h:1094 msgid "Center" msgstr "centre" #: fotoxx.h:1095 msgid "Choose" msgstr "Escollir" #: fotoxx.h:1096 msgid "Clear" msgstr "Esborrar" #: fotoxx.h:1097 msgid "Color" msgstr "Color" #: fotoxx.h:1098 msgid "continue" msgstr "continuar" #: fotoxx.h:1100 msgid "Copy" msgstr "Copiar" #: fotoxx.h:1101 msgid "Create" msgstr "Crear" #: fotoxx.h:1102 msgid "Curve File:" msgstr "Arxiu de corba:" #: fotoxx.h:1103 msgid "Cut" msgstr "Tallar" #: fotoxx.h:1104 msgid "Deband" msgstr "Eliminar bandes" #: fotoxx.h:1105 msgid "Delete" msgstr "Esborrar" #: fotoxx.h:1107 msgid "Done" msgstr "Fet" #: fotoxx.h:1108 msgid "edge" msgstr "vora" #: fotoxx.h:1111 msgid "Erase" msgstr "Esborrar" #: fotoxx.h:1112 msgid "Fetch" msgstr "Extreure" #: fotoxx.h:1113 msgid "output file already exists" msgstr "l'arxiu de sortida ja existeix" #: fotoxx.h:1114 #, c-format msgid "%d files selected" msgstr "%d arxius seleccionats" #: fotoxx.h:1115 msgid "Find" msgstr "Trobar" #: fotoxx.h:1116 msgid "Finish" msgstr "Acabar" #: fotoxx.h:1117 msgid "Flatten" msgstr "Aplanar" #: fotoxx.h:1118 msgid "Font" msgstr "Tipus de lletra" #: fotoxx.h:1119 msgid "Geotags" msgstr "Geo-etiquetes" #: fotoxx.h:1121 msgid "Grid" msgstr "Graella" #: fotoxx.h:1122 msgid "Height" msgstr "Alçada" #: fotoxx.h:1125 msgid "Images" msgstr " imatges" #: fotoxx.h:1126 msgid "Insert" msgstr "Insertar" #: fotoxx.h:1129 zfuncs.cc:10102 msgid "left" msgstr "esquerra" #: fotoxx.h:1130 msgid "limit" msgstr "limit" #: fotoxx.h:1131 msgid "Load" msgstr "Carregar" #: fotoxx.h:1132 msgid "Make" msgstr "Fer" #: fotoxx.h:1134 msgid "Map" msgstr "Mapa" #: fotoxx.h:1135 msgid "Max" msgstr "Max" #: fotoxx.h:1136 msgid "Negative" msgstr "Negatiu" #: fotoxx.h:1137 msgid "New" msgstr "Nou" #: fotoxx.h:1138 msgid "Next" msgstr "Següent" #: fotoxx.h:1139 zfuncs.cc:9190 msgid "No" msgstr "No" #: fotoxx.h:1140 msgid "no images" msgstr "sense imatges" #: fotoxx.h:1142 msgid "no selection" msgstr "sense selecció" #: fotoxx.h:1143 msgid "OK" msgstr "OK" #: fotoxx.h:1145 msgid "Paste" msgstr "Enganxar" #: fotoxx.h:1146 msgid "Pause" msgstr "Pausa" #: fotoxx.h:1147 msgid "Percent" msgstr "Percentatge" #: fotoxx.h:1149 msgid "Presets" msgstr "Predefinits" #: fotoxx.h:1150 msgid "Prev" msgstr "Anterior" #: fotoxx.h:1151 msgid "Proceed" msgstr "Procedir" #: fotoxx.h:1153 msgid "range" msgstr "rang" #: fotoxx.h:1156 msgid "Reduce" msgstr "Reduïr" #: fotoxx.h:1157 msgid "Remove" msgstr "Eliminar" #: fotoxx.h:1159 msgid "Replace" msgstr "" #: fotoxx.h:1160 msgid "Reserved" msgstr "Reservat" #: fotoxx.h:1161 msgid "Reset" msgstr "Restablir" #: fotoxx.h:1162 zfuncs.cc:10106 msgid "right" msgstr "dreta" #: fotoxx.h:1163 msgid "Rotate" msgstr "Rotar" #: fotoxx.h:1165 msgid "Unknown file type, save as tiff/jpeg/png to edit" msgstr "Tipus d'arxiu desconegut, guardar com a TIFF/JPEG/PNG per editar" #: fotoxx.h:1166 msgid "Search" msgstr "Buscar" #: fotoxx.h:1167 msgid "Seconds" msgstr "Segons" #: fotoxx.h:1171 msgid "Size" msgstr "Tamany" #: fotoxx.h:1172 msgid "Start" msgstr "Començar" #: fotoxx.h:1173 msgid "Strength" msgstr "Força" #: fotoxx.h:1174 msgid "Threshold" msgstr "Llindar" #: fotoxx.h:1175 #, c-format msgid "exceed %d files" msgstr "excedeix %d arxius" #: fotoxx.h:1176 zfuncs.cc:10094 msgid "top" msgstr "adalt" #: fotoxx.h:1177 msgid "Transparency" msgstr "Transparència" #: fotoxx.h:1179 msgid "Trim" msgstr "Retallar" #: fotoxx.h:1180 msgid "Undo All" msgstr "Desfer-ho tot" #: fotoxx.h:1181 msgid "Undo Last" msgstr "Desfer l'últim" #: fotoxx.h:1183 msgid "Unfinish" msgstr "No acabat" #: fotoxx.h:1185 msgid "View" msgstr "Veure" #: fotoxx.h:1186 msgid "Web" msgstr "Web" #: fotoxx.h:1187 msgid "White" msgstr "Blanc" #: fotoxx.h:1188 msgid "Width" msgstr "Ample" #: fotoxx.h:1189 msgid "x-offset" msgstr "desplaçament X" #: fotoxx.h:1190 msgid "y-offset" msgstr "desplaçament Y" #: fotoxx.h:1191 zfuncs.cc:9190 msgid "Yes" msgstr "Si" #: zfuncs.cc:1601 #, c-format msgid "" "create directory? \n" " %s" msgstr "Crear directori? %s" #: zfuncs.cc:4378 msgid "user guide not found" msgstr "no trobada la guia d'usuari" #: zfuncs.cc:5196 #, c-format msgid "cannot open file %s" msgstr "no es pot obrir l'arxiu %s" #: zfuncs.cc:5232 msgid "save screen to file" msgstr "guardar la pantalla en un arxiu" #: zfuncs.cc:9266 zfuncs.cc:9761 zfuncs.cc:10081 msgid "cancel" msgstr "cancel.lar" #: zfuncs.cc:9722 msgid "choose file" msgstr "escollir arxiu" #: zfuncs.cc:9727 msgid "choose files" msgstr "escollir arxius" #: zfuncs.cc:9732 msgid "save" msgstr "guardar" #: zfuncs.cc:9738 msgid "choose folder" msgstr "escollir carpeta" #: zfuncs.cc:9743 msgid "choose folders" msgstr "escollir carpetes" #: zfuncs.cc:9748 msgid "create folder" msgstr "crear carpeta" #: zfuncs.cc:9755 msgid "hidden" msgstr "ocult" #: zfuncs.cc:10081 msgid "done" msgstr "fet" #: zfuncs.cc:10081 zfuncs.cc:10090 msgid "margins" msgstr "marges" #: zfuncs.cc:10111 msgid "image scale" msgstr "escala de l'imatge" #: zfuncs.cc:10113 msgid "percent" msgstr "percentatge" #: zfuncs.cc:10120 msgid "image" msgstr "imatge" #: zfuncs.cc:10124 msgid "width" msgstr "amplada" #: zfuncs.cc:10128 msgid "height" msgstr "alçada" #~ msgid "(0 images)" #~ msgstr "(0 imatges)" #~ msgid "(%d images)" #~ msgstr "(%d imatges)" #~ msgid "jpeg quality must be 1-100" #~ msgstr "qualitat JPEG ha de ser 1-100" #~ msgid "Albums: Manage Albums, Slide Show, Desktop Background" #~ msgstr "Àlbums: Administrar àlbums, Diaporama, Fons d'esciptori" #~ msgid "Set desktop image from current Fotoxx image" #~ msgstr "Estaablir fons d'esciptori des de l'actual imatge de Fotoxx" #~ msgid "Cycle desktop images from a Fotoxx album" #~ msgstr "Fons d'escriptori cíclics de d'un àlbum de Fotoxx" #~ msgid "Set Desktop Image" #~ msgstr "Establir fons d'escriptori" #~ msgid "Cycle Desktop Image" #~ msgstr "Fons d'escriptori cíclic" #~ msgid "" #~ "new name/base/adder unreasonable\n" #~ " e.g. newname ### 100 10" #~ msgstr "" #~ "nou nom/inici/increment no raonable\n" #~ "p.e. nounom ### 100 10" #~ msgid "Adjust Brightness Dist." #~ msgstr "Ajustar hitograma" #~ msgid "Median Brightness" #~ msgstr "Mediana de brillantor" #~ msgid "Flatten Outliers 2" #~ msgstr "Aplanar contorns 2" #~ msgid "Flatten Outliers 1" #~ msgstr "Aplanar contorns 1" #~ msgid "Kuwahara method" #~ msgstr "Mètode Kuwahara" #~ msgid "brightness gradient" #~ msgstr "gradient de brillantor" #~ msgid "unsharp mask" #~ msgstr "màscara de desenfocament" #~ msgid "Lock aspect ratio" #~ msgstr "Bloquejar proporció d'aspecte" fotoxx-15.11.1/locales/translate-it.po0000664000175000017500000032424512616075370016263 0ustar micomico# translation of fotoxx.po to italian # Eugenio Baldi , 2009. # (revisione) Doriano Blengino , 2011-2015 # #-#-#-#-# fotoxx.po (messages) #-#-#-#-# # Italian translations for home package. # Copyright (C) 2008 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: 2015-10-28 22:08+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:78 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:80 msgid "Drag album thumbnail to new position." msgstr "Trascinare l'icona dell'album in nuova posizione" #: f.albums.cc:109 f.widgets.cc:548 msgid "Manage Albums" msgstr "Gestisci gli Album" #: f.albums.cc:120 f.albums.cc:258 msgid "Create or replace an album" msgstr "Crea o rimpiazza un album" #: f.albums.cc:122 msgid "Album to view or edit" msgstr "Album da vedere o modificare" #: f.albums.cc:124 msgid "Select images, add to cache" msgstr "Selezionare immagini, aggiungere al deposito" #: f.albums.cc:126 msgid "Clear image cache" msgstr "Azzera il deposito immagini" #: f.albums.cc:128 f.albums.cc:150 msgid "Delete an album" msgstr "Elimina un album" #: f.albums.cc:149 msgid "Choose Album" msgstr "Scegli un album" #: f.albums.cc:151 #, c-format msgid "Image cache has %d images" msgstr "" #: f.albums.cc:152 msgid "cache added to empty album" msgstr "Il deposito è stato aggiunto a un album vuoto" #: f.albums.cc:207 #, c-format msgid "delete %s ?" msgstr "Eliminare %s?" #: f.albums.cc:242 #, c-format msgid "fill from image cache (%d images)" msgstr "Riempire da deposito immagini (%d immagini)" #: f.albums.cc:260 f.albums.cc:297 msgid "Album Name" msgstr "Nome dell'album" #: f.albums.cc:266 msgid "make an initially empty album" msgstr "Crea un album vuoto" #: f.albums.cc:268 msgid "fill from current gallery" msgstr "Riempire con la galleria corrente" #: f.albums.cc:311 msgid "enter an album name" msgstr "Immettere un nome per l'album" #: f.albums.cc:338 msgid "gallery is empty" msgstr "La galleria è vuota" #: f.albums.cc:365 msgid "new album created" msgstr "Creato un nuovo album" #: f.albums.cc:1099 msgid "Press ESC to exit slide show" msgstr "Premi ESC per terminare lo slide show" #: f.albums.cc:1101 msgid "instant" msgstr "istantanea" #: f.albums.cc:1102 msgid "fade-in" msgstr "dissolvenza" #: f.albums.cc:1103 msgid "roll-right" msgstr "tenda-sx-dx" #: f.albums.cc:1104 msgid "roll-down" msgstr "tenda-su-giu" #: f.albums.cc:1105 msgid "venetian" msgstr "Veneziana" #: f.albums.cc:1106 msgid "grate" msgstr "quadrettoni" #: f.albums.cc:1107 msgid "rectangle" msgstr "rettangolo" #: f.albums.cc:1108 msgid "implode" msgstr "implodi" #: f.albums.cc:1109 msgid "explode" msgstr "esplodi" #: f.albums.cc:1110 msgid "radar" msgstr "radar" #: f.albums.cc:1111 msgid "Japanese-fan" msgstr "FanGiapponese" #: f.albums.cc:1112 msgid "jaws" msgstr "ganasce" #: f.albums.cc:1113 msgid "ellipse" msgstr "ellisse" #: f.albums.cc:1114 msgid "raindrops" msgstr "gocce" #: f.albums.cc:1115 msgid "doubledoor" msgstr "Finestra-doppia" #: f.albums.cc:1116 msgid "rotate" msgstr "ruotare" #: f.albums.cc:1117 msgid "fallover" msgstr "pioggia" #: f.albums.cc:1118 msgid "sphereoid" msgstr "" #: f.albums.cc:1139 f.widgets.cc:549 msgid "Slide Show" msgstr "Slideshow" #: f.albums.cc:1150 msgid "Clip Limit" msgstr "Limite dimensione" #: f.albums.cc:1154 msgid "Music File" msgstr "File musicale" #: f.albums.cc:1159 msgid "Full Screen" msgstr "Pieno schermo" #: f.albums.cc:1160 msgid "Auto-replay" msgstr "Riparte automaticamente" #: f.albums.cc:1166 msgid "Customize:" msgstr "Personalizza:" #: f.albums.cc:1167 msgid "transitions" msgstr "transizioni" #: f.albums.cc:1168 msgid "image files" msgstr "immagini" #: f.albums.cc:1173 f.albums.cc:1289 #, c-format msgid "%d images" msgstr "%d immagini" #: f.albums.cc:1195 f.albums.cc:1259 f.albums.cc:1349 f.albums.cc:1462 #: f.albums.cc:1655 f.albums.cc:1739 f.albums.cc:1756 f.albums.cc:1977 msgid "invalid album" msgstr "album non valido" #: f.albums.cc:1273 msgid "open album" msgstr "apri album" #: f.albums.cc:1305 msgid "Select music file" msgstr "Seleziona file musicale" #: f.albums.cc:1329 msgid "select random (if 5+ enabled)" msgstr "casuale (se più di 5)" #: f.albums.cc:1353 msgid "Transition Preferences" msgstr "Preferenze di transizione" #: f.albums.cc:1362 msgid "transition" msgstr "Effetto di transizione" #: f.albums.cc:1363 msgid "enabled" msgstr "Attiva" #: f.albums.cc:1364 msgid "slowdown" msgstr "Rallenta" #: f.albums.cc:1365 msgid "preference" msgstr "Preferenza" #: f.albums.cc:1468 msgid "Image Preferences" msgstr "Preferenze d'immagine" #: f.albums.cc:1472 f.file.cc:1159 f.file.cc:1400 f.file.cc:1608 f.meta.cc:437 msgid "Image File:" msgstr "File immagine:" #: f.albums.cc:1476 msgid "Play tone when image shows" msgstr "Suona quando l'immagine appare" #: f.albums.cc:1479 msgid "Show image caption" msgstr "Mostra titolo" #: f.albums.cc:1484 msgid "Show image comments" msgstr "Mostra commenti" #: f.albums.cc:1489 msgid "Wait before zoom" msgstr "Attendi prima d'ingrandire" #: f.albums.cc:1494 msgid "Zoom type:" msgstr "Tipo di zoom:" #: f.albums.cc:1495 msgid "none" msgstr "nessuno" #: f.albums.cc:1496 msgid "zoom-in" msgstr "ingrandisci" #: f.albums.cc:1497 msgid "zoom-out" msgstr "riduci" #: f.albums.cc:1500 msgid "Zoom size (x)" msgstr "Ingrandimento" #: f.albums.cc:1503 msgid "Steps" msgstr "N. passi" #: f.albums.cc:1507 msgid "Zoom image location:" msgstr "Fuoco dell'ingrandimento" #: f.albums.cc:1515 msgid "Wait after zoom" msgstr "Attendi dopo zoom" #: f.albums.cc:1520 msgid "Transition to next image" msgstr "Transizione alla prossima immagine" #: f.albums.cc:1523 f.albums.cc:1625 msgid "next" msgstr "prossima" #: f.albums.cc:1962 f.albums.cc:2120 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "formato file errato: \n" " %s" #: f.area.cc:79 msgid "Select Area for Edits" msgstr "Seleziona area per modifiche" #: f.area.cc:80 f.edit.cc:5927 msgid "Press F1 for help" msgstr "Premi F1 per l'aiuto" #: f.area.cc:89 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Questa funzione non supporta \n" "aree di selezione." #: f.area.cc:119 msgid "select rectangle" msgstr "Sel. rettangolo" #: f.area.cc:120 msgid "select ellipse" msgstr "Sel. ellisse" #: f.area.cc:123 msgid "freehand draw" msgstr "Mano libera" #: f.area.cc:124 msgid "follow edge" msgstr "Insegui contorno" #: f.area.cc:125 msgid "draw/replace" msgstr "Disegna e ritocca" #: f.area.cc:128 msgid "select area within mouse" msgstr "Seleziona area col mouse" #: f.area.cc:131 msgid "select one matching color within mouse" msgstr "Seleziona un colore simile col mouse" #: f.area.cc:135 msgid "select all matching colors within mouse" msgstr "Seleziona tutti i colori entro il mouse" #: f.area.cc:139 f.edit.cc:5972 msgid "mouse radius" msgstr "Raggio del mouse:" #: f.area.cc:142 msgid "match level %" msgstr "Similarità %" #: f.area.cc:147 msgid "search range" msgstr "Raggio di ricerca" #: f.area.cc:149 msgid "firewall" msgstr "Confinamento" #: f.area.cc:152 msgid "Area Edge Blend Width" msgstr "Larghezza sfumatura dell'area" #: f.area.cc:155 msgid "Edge Creep" msgstr "Ritocco lati (dentro/fuori)" #: f.area.cc:160 msgid "Line Color:" msgstr "Colore linea:" #: f.area.cc:180 f.area.cc:181 msgid "area edits fade away within edge distance" msgstr "Le modifiche sfumano entro la distanza dai bordi" #: f.area.cc:360 f.area.cc:543 #, c-format msgid "exceed %d edits" msgstr "superate %d modifiche" #: f.area.cc:1364 msgid "" "Click one time inside each enclosed area \n" "(possible gaps in the outline will be found). \n" "Press F1 for help." msgstr "" "Clicca ogni singola area chiusa: \n" "eventuali interruzioni nel contorno saranno rilevate. \n" "Premi F1 per l'aiuto" #: f.area.cc:1392 msgid "finish area" msgstr "Finisci area" #: f.area.cc:1400 msgid "extend to corner:" msgstr "estendi agli angoli:" #: f.area.cc:1441 msgid "searching" msgstr "in ricerca" #: f.area.cc:1472 msgid "outline has a gap" msgstr "il contorno è interrotto" #: f.area.cc:1476 msgid "success" msgstr "Successo" #: f.area.cc:1613 f.area.cc:1640 #, c-format msgid "found %d pixels" msgstr "trovati %d pixel" #: f.area.cc:1935 f.area.cc:1952 f.area.cc:1972 f.area.cc:2287 f.area.cc:2363 #: f.area.cc:2520 msgid "the area is not finished" msgstr "L'area non è chiusa" #: f.area.cc:2061 msgid "Edge calculation in progress" msgstr "Ricalcolo bordo in corso" #: f.area.cc:2070 msgid "Area Edge Calc" msgstr "Calcolo area delimitata" #: f.area.cc:2471 msgid "load area from a file" msgstr "carica area da un file" #: f.area.cc:2554 msgid "save area as a PNG file" msgstr "salva area come file PNG" #: f.area.cc:2582 msgid "position with mouse click/drag" msgstr "posizione con clic o trascinamento" #: f.area.cc:2638 msgid "Paste Image" msgstr "Incolla immagine" #: f.area.cc:2643 f.batch.cc:1169 f.batch.cc:1660 msgid "resize" msgstr "ridimensiona" #: f.batch.cc:107 f.widgets.cc:531 msgid "Batch Convert" msgstr "Conversione in massa" #: f.batch.cc:114 f.file.cc:942 msgid "New Name" msgstr "Nuovo nome" #: f.batch.cc:118 msgid "Sequence Numbers" msgstr "" #: f.batch.cc:120 msgid "base" msgstr "comune" #: f.batch.cc:122 msgid "adder" msgstr "addizione" #: f.batch.cc:127 msgid "New Location" msgstr "Nuova destinazione:" #: f.batch.cc:132 msgid "New File Type" msgstr "Nuovo tipo di file" #: f.batch.cc:136 f.batch.cc:144 f.repair.cc:3861 msgid "no change" msgstr "NON modificare" #: f.batch.cc:139 msgid "max. Width" msgstr "Larghezza max" #: f.batch.cc:147 f.batch.cc:565 msgid "Delete Originals" msgstr "Elimina gli originali" #: f.batch.cc:148 f.batch.cc:562 msgid "Copy Metadata" msgstr "Copia i metadati" #: f.batch.cc:149 f.batch.cc:563 f.edit.cc:1413 f.widgets.cc:438 #: f.widgets.cc:684 msgid "Upright" msgstr "Raddrizza" #: f.batch.cc:152 f.batch.cc:564 f.batch.cc:1178 f.batch.cc:1669 #: f.repair.cc:118 f.widgets.cc:454 msgid "Sharpen" msgstr "Contrasta" #: f.batch.cc:154 f.batch.cc:1180 f.batch.cc:1671 msgid "amount" msgstr "Quantità" #: f.batch.cc:157 f.batch.cc:1183 f.batch.cc:1674 msgid "threshold" msgstr "Soglia" #: f.batch.cc:280 msgid "file type not supported" msgstr "tipo di file non gestito" #: f.batch.cc:344 msgid "cannot create new file" msgstr "Errore in creazione del nuovo file" #: f.batch.cc:391 msgid "updating albums ..." msgstr "aggiornamento degli album..." #: f.batch.cc:464 f.batch.cc:1456 f.batch.cc:1862 f.file.cc:1208 msgid "Select directory" msgstr "Seleziona cartella" #: f.batch.cc:513 msgid "" "new name/base/adder unreasonable\n" " e.g. newname ### 100 10\n" " or ... [oldname] ..." msgstr "" #: f.batch.cc:537 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "Dimensioni massime %d x %d non ragionevoli" #: f.batch.cc:556 #, c-format msgid "Convert %d image files" msgstr "Conversione %d file immagine" #: f.batch.cc:557 msgid "Rename to" msgstr "Rinomina come" #: f.batch.cc:558 msgid "Convert to" msgstr "Converti a" #: f.batch.cc:559 msgid "Resize within" msgstr "Ridimensiona entro" #: f.batch.cc:560 msgid "Output to" msgstr "Produci su" #: f.batch.cc:566 msgid "PROCEED?" msgstr "PROSEGUO?" #: f.batch.cc:720 f.widgets.cc:532 msgid "Batch Upright" msgstr "Raddrizzare immagini in blocco" #: f.batch.cc:726 msgid "Survey all files" msgstr "Controlla tutti i file" #: f.batch.cc:780 msgid "file cannot be read" msgstr "Impossibile leggere il file" #: f.batch.cc:898 msgid "cannot select both options" msgstr "Non selezionare entrambe le opzioni" #: f.batch.cc:942 f.widgets.cc:533 msgid "Batch Delete/Trash" msgstr "Elimina/cestina in massa" #: f.batch.cc:947 msgid "delete" msgstr "Elimina" #: f.batch.cc:950 msgid "trash" msgstr "Cestina" #: f.batch.cc:1112 msgid "Batch Convert RAW Files (DCraw)" msgstr "Convertire immagini RAW (DCraw) in blocco" #: f.batch.cc:1121 msgid "DCraw not installed" msgstr "Programma DCraw non installato" #: f.batch.cc:1156 f.batch.cc:1647 msgid "output location" msgstr "Cartella o directory di uscita" #: f.batch.cc:1161 f.batch.cc:1652 msgid "output file type" msgstr "Tipo file d'uscita" #: f.batch.cc:1192 msgid "white balance" msgstr "bilanciamento del bianco" #: f.batch.cc:1193 msgid "interpolation" msgstr "interpolazione" #: f.batch.cc:1194 msgid "color space" msgstr "spazio di colore" #: f.batch.cc:1195 msgid "gamma curve" msgstr "curva del gamma" #: f.batch.cc:1198 msgid "camera" msgstr "fotocamera" #: f.batch.cc:1199 msgid "fixed" msgstr "fissa" #: f.batch.cc:1200 msgid "calculated" msgstr "calcolata" #: f.batch.cc:1217 msgid "default" msgstr "Standard" #: f.batch.cc:1223 msgid "defaults" msgstr "Standard" #: f.batch.cc:1610 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Conversione in massa di file RAW (Raw Therapee)" #: f.batch.cc:1619 f.file.cc:428 msgid "Raw Therapee not installed" msgstr "Programma Raw Therapee non installato" #: f.batch.cc:1959 msgid "start" msgstr "Inizia" #: f.batch.cc:1960 msgid "begin making a script file" msgstr "Inizia a comporre uno script" #: f.batch.cc:1962 msgid "close" msgstr "Chiudi" #: f.batch.cc:1963 msgid "finish making a script file" msgstr "Termina la composizione dello script" #: f.batch.cc:1965 msgid "run" msgstr "Avvia" #: f.batch.cc:1966 msgid "execute a script file" msgstr "Esegui uno script da file" #: f.batch.cc:2020 msgid "script already started" msgstr "Script già in esecuzione" #: f.batch.cc:2024 msgid "open new script file" msgstr "Apri un file script" #: f.batch.cc:2056 msgid "script file error" msgstr "Errore nel file script" #: f.batch.cc:2061 #, c-format msgid "%s added to script" msgstr "%s aggiunto allo script" #: f.batch.cc:2074 msgid "no script file was started" msgstr "Nessuno script è stato avviato" #: f.batch.cc:2082 msgid "script file closed" msgstr "File script chiuso" #: f.batch.cc:2101 msgid "script file is not closed" msgstr "File di script non chiuso" #: f.batch.cc:2105 msgid "select script file to run" msgstr "Selezionare il file script da eseguire" #: f.batch.cc:2107 msgid "open script file" msgstr "Aprire un file di script" #: f.batch.cc:2114 msgid "unknown script file" msgstr "File script sconosciuto" #: f.batch.cc:2121 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" "File script: %s\n" " %s" #: f.batch.cc:2132 msgid "select image files to be processed" msgstr "Selezionare le immagini da processare" #: f.batch.cc:2144 f.batch.cc:2193 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "Errore in apertura: %s \n" " %s" #: f.batch.cc:2165 #, c-format msgid "unknown edit function: %s" msgstr "Funzione di edit sconosciuta: %s" #: f.batch.cc:2174 #, c-format msgid "load widgets failed: %s" msgstr "Errore di caricamento widget: %s" #: f.batch.cc:2203 #, c-format msgid "script file format error: %s" msgstr "Formato file script errato: %s" #: f.batch.cc:2229 msgid "Brasero not installed" msgstr "Pacchetto 'brasero' non installato" #: f.batch.cc:2318 f.widgets.cc:538 msgid "Find Duplicate Images" msgstr "Trova immagini duplicate" #: f.batch.cc:2320 f.tools.cc:929 msgid "Thumbnail size" msgstr "Dimensione miniatura" #: f.batch.cc:2323 msgid "pixel difference" msgstr "Differenza di pixel" #: f.batch.cc:2326 msgid "pixel count" msgstr "Conteggio pixel" #: f.batch.cc:2329 msgid "Images:" msgstr "Immagini:" #: f.batch.cc:2330 msgid "searching ..." msgstr "Ricerca in corso..." #: f.batch.cc:2332 msgid "Duplicates:" msgstr "Duplicate:" #: f.batch.cc:2350 #, c-format msgid "only %d image thumbnails found" msgstr "Trovate solo %d miniature" #: f.bend.cc:86 f.widgets.cc:475 msgid "Unbend" msgstr "Correggi deformazioni" #: f.bend.cc:94 f.edit.cc:4267 f.effects.cc:4725 msgid "vertical" msgstr "Verticale" #: f.bend.cc:95 f.edit.cc:4266 f.effects.cc:4721 msgid "horizontal" msgstr "Orizzontale" #: f.bend.cc:96 msgid "linear" msgstr "Lineare" #: f.bend.cc:99 msgid "curved" msgstr "Curvato" #: f.bend.cc:367 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.bend.cc:384 msgid "Perspective Correction" msgstr "Correzione di prospettiva" #: f.bend.cc:590 msgid "must have 4 corners" msgstr "Occorrono 4 angoli." #: f.bend.cc:712 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.bend.cc:725 f.widgets.cc:477 msgid "Warp area" msgstr "Deforma area" #: f.bend.cc:730 msgid "start warp" msgstr "Avvia deformazione" #: f.bend.cc:788 msgid "no active Select Area" msgstr "Area di selezione non attiva" #: f.bend.cc:1129 f.bend.cc:1443 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.bend.cc:1147 f.widgets.cc:478 msgid "Warp curved" msgstr "Deforma a curva" #: f.bend.cc:1156 f.mashup.cc:1836 msgid "warp span" msgstr "Dimensione deformazione: " #: f.bend.cc:1461 f.widgets.cc:479 msgid "Warp linear" msgstr "Deforma a linea" #: f.bend.cc:1776 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.bend.cc:1792 f.widgets.cc:480 msgid "Warp affine" msgstr "Deforma specularmente" #: f.bend.cc:2138 msgid "Flatten Book Page Photo" msgstr "Appiattisci foto di pagina di libro" #: f.bend.cc:2139 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.bend.cc:2142 msgid "Stretch curved-down surfaces:" msgstr "Deforma le superfici curve" #: f.bend.cc:2197 msgid "top:" msgstr "Cima:" #: f.bend.cc:2200 msgid "bottom:" msgstr "Fondo:" #: f.combine.cc:2108 f.combine.cc:2740 f.combine.cc:3396 f.combine.cc:3928 msgid "Select 2 to 9 files" msgstr "Seleziona da 2 a 9 file" #: f.combine.cc:2129 f.combine.cc:2761 f.combine.cc:3417 f.combine.cc:3949 msgid "Images are not all the same size" msgstr "Le immagini non hanno la stessa dimensione" #: f.combine.cc:2473 msgid "Adjust Image Contributions" msgstr "Regola contributi immagine" #: f.combine.cc:2477 msgid "dark pixels" msgstr "Pixel scuri" #: f.combine.cc:2479 msgid "light pixels" msgstr "Pixel luminosi" #: f.combine.cc:2481 msgid "file:" msgstr "File:" #: f.combine.cc:2997 msgid "Paint and Warp Image" msgstr "Dipingi e deforma immagine" #: f.combine.cc:3001 f.combine.cc:3646 f.combine.cc:6237 f.effects.cc:3912 #: f.widgets.cc:567 f.widgets.cc:590 f.widgets.cc:612 msgid "Image" msgstr "Immagine" #: f.combine.cc:3005 msgid "paint" msgstr "Dipingi" #: f.combine.cc:3006 msgid "warp" msgstr "Deforma" #: f.combine.cc:3644 msgid "Select and Paint Image" msgstr "Seleziona e dipingi" #: f.combine.cc:4120 msgid "Adjust Pixel Composition" msgstr "Regola la composizione dei pixel" #: f.combine.cc:4122 msgid "use average" msgstr "Usa media" #: f.combine.cc:4123 msgid "use median" msgstr "Usa mediano" #: f.combine.cc:4125 msgid "omit low pixel" msgstr "ometti pixel bassi" #: f.combine.cc:4126 msgid "omit high pixel" msgstr "ometti pixel alti" #: f.combine.cc:4388 f.combine.cc:5494 msgid "Select 2 to 4 files" msgstr "Seleziona da 2 a 4 file" #: f.combine.cc:4463 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:4465 f.combine.cc:5571 msgid "no curve (scanned image)" msgstr "nessuna curva (immagine scansionata)" #: f.combine.cc:4466 f.combine.cc:5572 msgid "Search for lens mm" msgstr "Ricerca per mm lenti" #: f.combine.cc:4467 f.combine.cc:5573 msgid "Save lens mm → image EXIF" msgstr "Salva mm della lente nelle informazioni EXIF" #: f.combine.cc:4528 f.combine.cc:5634 msgid "Pre-align Images" msgstr "Pre-allineamento immagini" #: f.combine.cc:4532 f.combine.cc:5638 msgid "lens mm" msgstr "mm lente" #: f.combine.cc:4537 f.combine.cc:5643 msgid "no auto warp" msgstr "Deformazione automatica esclusa" #: f.combine.cc:4539 f.combine.cc:5645 f.widgets.cc:445 f.widgets.cc:683 msgid "Resize" msgstr "Ridimensiona" #: f.combine.cc:4540 f.combine.cc:5646 msgid "resize window" msgstr "Ridimensiona finestra" #: f.combine.cc:4548 f.combine.cc:5654 msgid "do not warp images during auto-alignment" msgstr "Non deformare immagini durante l'auto allineamento" #: f.combine.cc:4593 f.combine.cc:5699 msgid "use two images only" msgstr "Utilizza solo due immagini" #: f.combine.cc:4621 f.combine.cc:4825 f.combine.cc:5011 f.combine.cc:5725 #: f.combine.cc:5929 f.combine.cc:6115 msgid "Too little overlap, cannot align" msgstr "Troppo poca sovrapposizione, impossibile allineare" #: f.combine.cc:5089 f.combine.cc:6193 msgid "Match Brightness and Color" msgstr "Allinea luminosità e colore" #: f.combine.cc:5134 msgid "Select image" msgstr "Selezionare immagine" #: f.combine.cc:5149 f.combine.cc:6252 msgid "auto color" msgstr "colore automatico" #: f.combine.cc:5150 f.combine.cc:6253 msgid "file color" msgstr "file colore" #: f.combine.cc:5156 f.combine.cc:6259 msgid "mouse warp" msgstr "Distorcere col mouse" #: f.combine.cc:5159 msgid "curved image" msgstr "immagine curva" #: f.combine.cc:5569 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:6469 msgid "pano tools (hugin) not installed" msgstr "pano tools (hugin) non è installato" #: f.combine.cc:6478 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:173 f.widgets.cc:437 f.widgets.cc:682 msgid "Trim/Rotate" msgstr "Ritaglia/Ruota" #: f.edit.cc:185 f.edit.cc:586 msgid "ratio" msgstr "Rapporto:" #: f.edit.cc:189 msgid "trim size:" msgstr "Dimensioni del ritaglio:" #: f.edit.cc:194 msgid "Lock Ratio" msgstr "Blocca proporzioni" #: f.edit.cc:199 msgid "Customize" msgstr "Personalizza" #: f.edit.cc:207 msgid "Rotate: degrees" msgstr "Ruota: gradi" #: f.edit.cc:209 msgid "auto-trim" msgstr "Autoritaglio" #: f.edit.cc:582 msgid "Trim Buttons" msgstr "Pulsanti di taglio" #: f.edit.cc:585 msgid "label" msgstr "Etichetta" #: f.edit.cc:1411 msgid "Upright Image" msgstr "Raddrizza immagine" #: f.edit.cc:1477 msgid "rotation unknown" msgstr "Rotazione sconosciuta" #: f.edit.cc:2029 f.widgets.cc:441 f.widgets.cc:686 msgid "Retouch Combo" msgstr "Funzioni ritocco" #: f.edit.cc:2050 msgid "Amplifier" msgstr "Amplificatore" #: f.edit.cc:2051 fotoxx.h:1091 msgid "Brightness" msgstr "Luminosità" #: f.edit.cc:2052 f.effects.cc:3364 fotoxx.h:1099 msgid "Contrast" msgstr "Contrasto" #: f.edit.cc:2053 msgid "Low Color" msgstr "Poco colore" #: f.edit.cc:2054 msgid "Warmer" msgstr "Caldo" #: f.edit.cc:2055 f.effects.cc:1011 msgid "Dark Areas" msgstr "Aree scure" #: f.edit.cc:2064 msgid "Max." msgstr "Max" #: f.edit.cc:2065 f.edit.cc:2066 f.edit.cc:2067 msgid "High" msgstr "Alto" #: f.edit.cc:2068 msgid "Cooler" msgstr "Freddo" #: f.edit.cc:2069 msgid "Bright" msgstr "Luminose" #: f.edit.cc:2072 f.tools.cc:1631 msgid "Brightness Distribution" msgstr "Distribuzione della luminosità" #: f.edit.cc:2075 msgid "Click for white balance or black level" msgstr "Cliccare per bilanciamento del bianco/nero" #: f.edit.cc:2078 msgid "Settings File" msgstr "File di impostazioni" #: f.edit.cc:2082 msgid "recall previous settings used" msgstr "Richiama impostazioni usate in precedenza" #: f.edit.cc:2726 msgid "Adjust Brightness Distribution" msgstr "Regola la distribuzione di luminosità" #: f.edit.cc:2765 msgid "Low Cutoff" msgstr "Più scuri" #: f.edit.cc:2766 msgid "High Cutoff" msgstr "Più chiari" #: f.edit.cc:2767 msgid "Low Flatten" msgstr "Meno scuri" #: f.edit.cc:2768 msgid "Mid Flatten" msgstr "Più medi" #: f.edit.cc:2769 msgid "High Flatten" msgstr "Più chiari" #: f.edit.cc:2770 msgid "Low Stretch" msgstr "Comprimi scuri" #: f.edit.cc:2771 msgid "Mid Stretch" msgstr "Comprimi medi" #: f.edit.cc:2772 msgid "High Stretch" msgstr "Comprimi chiari" #: f.edit.cc:3102 msgid "Zonal Flatten Brightness" msgstr "Compressione luminosità a zone" #: f.edit.cc:3135 msgid "Zones" msgstr "Zone" #: f.edit.cc:3142 msgid "Deband Dark" msgstr "Escludi scuri" #: f.edit.cc:3145 msgid "Deband Bright" msgstr "Escludi chiari" #: f.edit.cc:3594 f.widgets.cc:444 f.widgets.cc:689 msgid "Tone Mapping" msgstr "Mappa tonalità" #: f.edit.cc:3625 msgid "low" msgstr "basso" #: f.edit.cc:3627 msgid "high" msgstr "alto" #: f.edit.cc:3630 msgid "Amplify" msgstr "Amplificazione" #: f.edit.cc:4034 msgid "Resize Image" msgstr "Ridimensiona" #: f.edit.cc:4059 msgid "W/H Ratio:" msgstr "" #: f.edit.cc:4061 msgid "Lock" msgstr "" #: f.edit.cc:4063 msgid "use previous settings" msgstr "Usa impostazioni esistenti" #: f.edit.cc:4262 f.widgets.cc:446 msgid "Flip" msgstr "Rifletti/specchia" #: f.edit.cc:4368 msgid "+Version" msgstr "+Versione" #: f.edit.cc:4391 msgid "Write Text on Image" msgstr "Scrivi testo su immagine" #: f.edit.cc:4392 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:4441 f.mashup.cc:2437 msgid "Text" msgstr "Testo" #: f.edit.cc:4446 msgid "Use metadata key" msgstr "Usa chiave metadati" #: f.edit.cc:4451 msgid "Use text file" msgstr "Usa file di testo" #: f.edit.cc:4470 f.mashup.cc:2455 msgid "text" msgstr "Testo" #: f.edit.cc:4471 f.edit.cc:5309 f.mashup.cc:2456 f.mashup.cc:2798 msgid "backing" msgstr "Sfondo" #: f.edit.cc:4472 f.edit.cc:5310 f.mashup.cc:2457 f.mashup.cc:2799 msgid "outline" msgstr "Contorno" #: f.edit.cc:4473 f.edit.cc:5311 f.mashup.cc:2458 f.mashup.cc:2800 msgid "shadow" msgstr "Ombra" #: f.edit.cc:4499 msgid "save to current file" msgstr "Salve nel file corrente" #: f.edit.cc:4500 f.file.cc:1909 msgid "save as new file version" msgstr "salva come nuova versione" #: f.edit.cc:4501 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:4662 f.mashup.cc:2557 f.tools.cc:1068 msgid "select font" msgstr "Scegli il font" #: f.edit.cc:4926 msgid "text file is defective" msgstr "file dei testi difettoso" #: f.edit.cc:5249 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:5280 msgid "Write Line or Arrow on Image" msgstr "Metti linea o freccia sull'immagine" #: f.edit.cc:5288 f.mashup.cc:2777 msgid "Line length" msgstr "Lunghezza linea" #: f.edit.cc:5295 f.mashup.cc:2784 msgid "Arrow head" msgstr "Terminazione freccia" #: f.edit.cc:5308 f.mashup.cc:2797 msgid "line" msgstr "Linea" #: f.edit.cc:5337 msgid "" "fix line/arrow in layout \n" " start new line/arrow" msgstr "" "Fissa linea/freccia\n" "inizia un'altra" #: f.edit.cc:5926 f.widgets.cc:449 msgid "Paint Edits" msgstr "Pennella funzione" #: f.edit.cc:5933 f.edit.cc:6162 fotoxx-15.11.cc:3179 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "L'area selezionata non può essere mantenuta.\n" "Continuare?" #: f.edit.cc:5941 f.edit.cc:6170 f.tools.cc:2157 msgid "Edit function must be active" msgstr "Una funzione di edit deve essere attiva" #: f.edit.cc:5946 msgid "Cannot use Paint Edits" msgstr "Impossibile usare Pennella Funzione" #: f.edit.cc:5975 msgid "power: center" msgstr "Forza: centro:" #: f.edit.cc:5980 msgid "reset area" msgstr "Azzera area" #: f.edit.cc:6155 f.widgets.cc:450 msgid "Leverage Edits" msgstr "Modula ritocco" #: f.edit.cc:6156 msgid "Edit Function Amplifier" msgstr "Regola amplificazione di funzione" #: f.edit.cc:6175 msgid "Cannot use Leverage Edits" msgstr "Impossibile usare Modula Ritocco" #: f.edit.cc:6208 msgid "minimum" msgstr "Minimo" #: f.edit.cc:6210 msgid "maximum" msgstr "Massimo" #: f.edit.cc:6510 msgid "Edit Plugins" msgstr "Gestione plug-in" #: f.edit.cc:6511 msgid "Edit plugins menu" msgstr "Modifica menu plug-in" #: f.edit.cc:6519 msgid "Run as Fotoxx edit function" msgstr "Esegui come funzione di edit fotoxx" #: f.edit.cc:6757 msgid "Plugin working ..." msgstr "Esecuzione plugin..." #: f.edit.cc:6766 msgid "plugin failed" msgstr "plugin non trovato" #: f.effects.cc:64 msgid "Set color depth to 1-16 bits" msgstr "Imposta la risoluzione del colore da 1 a 16 bit" #: f.effects.cc:76 msgid "Set Color Depth" msgstr "Imposta la risoluzione di colore" #: f.effects.cc:220 msgid "Convert to Sketch" msgstr "Converti in schizzo" #: f.effects.cc:296 msgid "Clip Level" msgstr "Bianco" #: f.effects.cc:300 msgid "Algorithm" msgstr "Metodo" #: f.effects.cc:305 msgid "Foreground" msgstr "Colore" #: f.effects.cc:308 msgid "Background" msgstr "Sfondo" #: f.effects.cc:714 f.widgets.cc:486 msgid "Line Drawing" msgstr "Tracciamento linea" #: f.effects.cc:727 msgid "black/white" msgstr "Bianco e nero" #: f.effects.cc:1005 f.widgets.cc:487 msgid "Color Drawing" msgstr "Tracciamento colori" #: f.effects.cc:1012 msgid "Bright Areas" msgstr "Aree luminose" #: f.effects.cc:1266 f.widgets.cc:488 msgid "Graduated Blur" msgstr "Sfumatura graduale" #: f.effects.cc:1270 msgid "Contrast Limit" msgstr "Limite contrasto" #: f.effects.cc:1273 f.repair.cc:775 msgid "Blur Radius" msgstr "Raggio di sfumatura" #: f.effects.cc:1487 msgid "Simulate Embossing" msgstr "Simula un bassorilievo" #: f.effects.cc:1504 msgid "depth" msgstr "Profondità" #: f.effects.cc:1706 msgid "Simulate Tiles" msgstr "Simula un mosaico" #: f.effects.cc:1710 msgid "tile size" msgstr "Dimensione tessere" #: f.effects.cc:1713 msgid "tile gap" msgstr "Distanza fra tessere" #: f.effects.cc:1716 msgid "3D depth" msgstr "Profondità 3D" #: f.effects.cc:1940 msgid "Convert Image to Dots" msgstr "Converti immagine in punti" #: f.effects.cc:1944 msgid "dot size" msgstr "Dimensione punto" #: f.effects.cc:2169 msgid "Simulate Painting" msgstr "Simula un dipinto" #: f.effects.cc:2173 msgid "color depth" msgstr "Profondità di colore" #: f.effects.cc:2177 msgid "patch area goal" msgstr "Fattore artistico" #: f.effects.cc:2181 msgid "req. color match" msgstr "Precisione colori" #: f.effects.cc:2185 msgid "borders" msgstr "Contorni" #: f.effects.cc:2758 f.widgets.cc:493 msgid "Vignette" msgstr "Effetto vignetta" #: f.effects.cc:3107 msgid "Add Texture" msgstr "Aggiungi disturbo" #: f.effects.cc:3324 msgid "Background Pattern" msgstr "Motivo di sfondo" #: f.effects.cc:3328 msgid "Pattern File:" msgstr "File motivo:" #: f.effects.cc:3333 msgid "Geometry" msgstr "Geometria" #: f.effects.cc:3334 msgid "Calculate" msgstr "Calcola" #: f.effects.cc:3336 msgid "Zoom" msgstr "Zoom" #: f.effects.cc:3347 f.widgets.cc:495 msgid "Pattern" msgstr "Motivo" #: f.effects.cc:3354 msgid "Overlap" msgstr "Sovrapposizione" #: f.effects.cc:3361 msgid "Opacity" msgstr "Opacità" #: f.effects.cc:3429 msgid "choose pattern tile" msgstr "Scelta tessera del motivo" #: f.effects.cc:3816 msgid "Create Mosaic" msgstr "Creazione Mosaico" #: f.effects.cc:3902 msgid "Tile" msgstr "Tessera" #: f.effects.cc:3910 f.widgets.cc:490 msgid "Tiles" msgstr "Tessere" #: f.effects.cc:3916 msgid "Tile blending" msgstr "Miscelazione tessere" #: f.effects.cc:3996 #, c-format msgid "exceeded max. tiles: %d" msgstr "Superato massimo tessere: %d" #: f.effects.cc:4011 #, c-format msgid "only %d tile images found" msgstr "Trovate solo %d tessere" #: f.effects.cc:4404 f.widgets.cc:497 msgid "Custom Kernel" msgstr "Matrice di trasformazione" #: f.effects.cc:4408 msgid "Kernel size" msgstr "Dimensione matrice" #: f.effects.cc:4411 msgid "Divisor" msgstr "Divisore" #: f.effects.cc:4414 msgid "Data file" msgstr "File di dati" #: f.effects.cc:4450 fotoxx-15.11.cc:3440 msgid "Load settings from file" msgstr "Carica impostazioni dal file" #: f.effects.cc:4711 f.widgets.cc:498 msgid "Make Waves" msgstr "Generare onde" #: f.effects.cc:4718 msgid "wavelength" msgstr "Lunghezza onda" #: f.effects.cc:4719 msgid "amplitude" msgstr "Ampiezza" #: f.effects.cc:4720 msgid "variance" msgstr "Varianza" #: f.effects.cc:4731 msgid "perspective" msgstr "Prospettiva" #: f.effects.cc:4905 msgid "Pull image using the mouse." msgstr "Tira l'immagine con il mouse" #: f.effects.cc:4926 f.widgets.cc:499 msgid "Directed Blur" msgstr "Sfumatura interattiva" #: f.effects.cc:4930 msgid "blur span" msgstr "Dimensione" #: f.effects.cc:4933 msgid "intensity" msgstr "Intensità" #: f.effects.cc:5099 f.widgets.cc:500 msgid "Spherical Projection" msgstr "" #: f.effects.cc:5143 msgid "Magnify" msgstr "" #: f.file.cc:186 msgid "" "use EXIF photo date or \n" " file modification date" msgstr "" "Usa data EXIF o \n" " data del file" #: f.file.cc:203 f.widgets.cc:572 msgid "File" msgstr "File" #: f.file.cc:357 msgid "UFraw not installed" msgstr "UFraw not installed" #: f.file.cc:373 f.widgets.cc:691 msgid "Open RAW file (ufraw)" msgstr "Apri file RAW (ufraw)" #: f.file.cc:378 f.file.cc:449 msgid "RAW type not registered in User Settings" msgstr "Tipo RAW non registrato in preferenze utente" #: f.file.cc:444 f.widgets.cc:692 msgid "Open RAW file (Raw Therapee)" msgstr "Apri file RAW (Raw Therapee)" #: f.file.cc:541 f.widgets.cc:395 msgid "Open Image File" msgstr "Apri immagine" #: f.file.cc:560 msgid "unknown file type" msgstr "tipo di file sconosciuto" #: f.file.cc:753 msgid "Create Blank Image" msgstr "Crea immagine vuota" #: f.file.cc:755 msgid "file name" msgstr "Nome file" #: f.file.cc:788 msgid "supply a file name" msgstr "fornire un nome di file" #: f.file.cc:934 f.widgets.cc:400 msgid "Rename Image File" msgstr "Rinomina immagine" #: f.file.cc:941 msgid "Old Name" msgstr "Vecchio nome" #: f.file.cc:951 msgid "previous name" msgstr "Nome precedente" #: f.file.cc:952 msgid "add 1" msgstr "aggiungi 1" #: f.file.cc:1120 msgid "Copy Image File" msgstr "Copia file" #: f.file.cc:1121 msgid "Move Image File" msgstr "Sposta file" #: f.file.cc:1164 msgid "new location" msgstr "nuova destinazione" #: f.file.cc:1236 msgid "new location is not a directory" msgstr "la destinazione non è una cartella" #: f.file.cc:1276 f.file.cc:1518 f.file.cc:1656 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "errore in cancellazione: \n" " %s" #: f.file.cc:1363 f.widgets.cc:401 msgid "Trash Image File" msgstr "Cestina l'immagine" #: f.file.cc:1364 f.file.cc:1571 msgid "(automatic step to next image)" msgstr "(avanzamento automatico alla prossima foto)" #: f.file.cc:1432 msgid "" "Linux standard trash does not work. \n" "Desktop trash folder will be created." msgstr "" "Il cestino standard di Linux non funziona. \n" "Verrà creato un cestino nel desktop" #: f.file.cc:1435 msgid "" "Linux and Desktop trash do not work. \n" "Permanently delete the image file?" msgstr "" "I cestini (di Linux e sul desktop) non funzionano. \n" "Eliminare definitivamente il file?" #: f.file.cc:1438 #, c-format msgid "Cannot create trash folder: %s" msgstr "Impossible creare il cestino: %s" #: f.file.cc:1450 msgid "Move read-only file to trash?" msgstr "Spostare il file in sola lettura nel cestino?" #: f.file.cc:1505 #, c-format msgid "error: %s" msgstr "errore: %s" #: f.file.cc:1534 f.file.cc:1673 f.file.cc:2508 msgid "no more images" msgstr "fine delle immagini" #: f.file.cc:1570 msgid "Delete Image File - CANNOT BE REVERSED" msgstr "Cancella file - NON SI PUO' ANNULLARE" #: f.file.cc:1646 msgid "Delete read-only file?" msgstr "Eliminare il file di sola lettura?" #: f.file.cc:1891 msgid "Save Image File" msgstr "Salva file immagine" #: f.file.cc:1901 msgid "new version" msgstr "nuova versione" #: f.file.cc:1902 msgid "new file" msgstr "Nuovo file" #: f.file.cc:1903 msgid "replace file" msgstr "rimpiazza file" #: f.file.cc:1910 f.file.cc:2281 msgid "save as new file name or type" msgstr "salva come nuovo nome o diverso tipo" #: f.file.cc:1912 msgid "replace old file (OVERWRITE)" msgstr "rimpiazza il vecchio file (SOVRASCRIVE)" #: f.file.cc:1950 f.file.cc:2057 msgid "cannot save as RAW type" msgstr "impossibile salvare come tipo RAW" #: f.file.cc:2051 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:2191 msgid "Unable to copy EXIF/IPTC data" msgstr "Non riesco a copiare i dati EXIF/IPTC" #: f.file.cc:2299 f.file.cc:2302 msgid "make current" msgstr "Rendi corrente" #: f.file.cc:2300 msgid "(new file becomes current file)" msgstr "" #: f.file.cc:2409 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Sovrascrivere il file? \n" " %s" #: f.file.cc:2555 f.widgets.cc:554 msgid "Quick Start" msgstr "Introduzione rapida" #: f.file.cc:2558 f.widgets.cc:555 msgid "User Guide" msgstr "Guida utente" #: f.file.cc:2561 f.widgets.cc:556 msgid "User Guide Changes" msgstr "Cronistoria della guida utente" #: f.file.cc:2564 f.widgets.cc:558 msgid "README" msgstr "Read-me" #: f.file.cc:2567 f.widgets.cc:557 msgid "Edit Functions Summary" msgstr "Sommario funzioni" #: f.file.cc:2570 f.widgets.cc:559 msgid "Change Log" msgstr "Cronistoria delle modifiche" #: f.file.cc:2573 f.widgets.cc:560 msgid "Log File" msgstr "Mostra file di log" #: f.file.cc:2576 f.widgets.cc:561 msgid "Translations" msgstr "Come tradurre" #: f.file.cc:2579 f.widgets.cc:562 msgid "Home Page" msgstr "Sito WEB" #: f.file.cc:2582 f.widgets.cc:563 msgid "About" msgstr "Info" #: f.file.cc:2586 f.widgets.cc:584 f.widgets.cc:606 fotoxx.h:1123 msgid "Help" msgstr "Aiuto" #: f.file.cc:2739 f.file.cc:2808 #, c-format msgid "file not found: %s" msgstr "file non trovato: %s" #: f.file.cc:2745 f.file.cc:2814 #, c-format msgid "file type not supported: %s" msgstr "tipo di file non supportato: %s" #: f.gallery.cc:918 f.gallery.cc:1007 f.widgets.cc:604 msgid "Scroll" msgstr "Scorri" #: f.gallery.cc:922 msgid "Sync" msgstr "Sinc" #: f.gallery.cc:932 f.repair.cc:5845 f.repair.cc:5850 fotoxx.h:1144 msgid "Open" msgstr "Apri" #: f.gallery.cc:933 msgid "change directory" msgstr "cambia cartella" #: f.gallery.cc:941 msgid "GoTo" msgstr "Vai a..." #: f.gallery.cc:947 f.widgets.cc:599 msgid "Sort" msgstr "Riordina" #: f.gallery.cc:953 f.gallery.cc:1517 f.gallery.cc:1518 f.gallery.cc:1520 #: f.widgets.cc:597 msgid "Zoom+" msgstr "Zoom+" #: f.gallery.cc:963 f.gallery.cc:1519 f.gallery.cc:1521 f.widgets.cc:598 msgid "Zoom-" msgstr "Zoom-" #: f.gallery.cc:975 f.gallery.cc:1525 msgid "Row↑" msgstr "Riga↑" #: f.gallery.cc:983 f.gallery.cc:1526 msgid "Row↓" msgstr "Riga↓" #: f.gallery.cc:991 f.gallery.cc:1523 f.gallery.cc:1530 f.widgets.cc:602 msgid "Page↑" msgstr "Pagina↑" #: f.gallery.cc:999 f.gallery.cc:1524 f.gallery.cc:1531 f.widgets.cc:603 msgid "Page↓" msgstr "Pagina↓" #: f.gallery.cc:1013 f.gallery.cc:1528 f.widgets.cc:600 msgid "First" msgstr "Prima" #: f.gallery.cc:1014 f.gallery.cc:1529 f.widgets.cc:601 msgid "Last" msgstr "Ultima" #: f.gallery.cc:1127 msgid "Choose image directory" msgstr "Scegliere directory immagini" #: f.gallery.cc:1134 f.gallery.cc:1148 msgid "recent" msgstr "recente" #: f.gallery.cc:1135 f.gallery.cc:1153 msgid "newest" msgstr "più nuovo" #: f.gallery.cc:1217 msgid "no albums found" msgstr "nessun album trovato" #: f.gallery.cc:1224 msgid "Choose album" msgstr "Scegliere album" #: f.gallery.cc:1274 msgid "Gallery Sort" msgstr "Riordina galleria" #: f.gallery.cc:1278 msgid "File Name" msgstr "Nome file" #: f.gallery.cc:1279 msgid "File Mod Date/Time" msgstr "Data di modifica file" #: f.gallery.cc:1280 msgid "Photo Date/Time (EXIF)" msgstr "Data della foto (EXIF)" #: f.gallery.cc:1282 msgid "ascending" msgstr "ascendente" #: f.gallery.cc:1283 msgid "descending" msgstr "discendente" #: f.gallery.cc:2212 fotoxx.h:1169 msgid "Select Files" msgstr "Seleziona i file" #: f.gallery.cc:2672 msgid "Click list position. Click thumbnail to add." msgstr "" "Clicca la lista per indicare la posizione; clicca le miniature per " "aggiungere." #: f.gallery.cc:2698 f.gallery.cc:2946 msgid "Edit Bookmarks" msgstr "Gestisci segnalibri" #: f.gallery.cc:2873 msgid "unable to save bookmarks file" msgstr "Impossibile salvare i segnalibri" #: f.gallery.cc:2946 msgid "Go To Bookmark" msgstr "Va al segnalibro" #: f.gallery.cc:3003 f.meta.cc:2844 f.repair.cc:7473 msgid "file not found" msgstr "File non trovato" #: 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:241 msgid "choose layout file" msgstr "Scegliere un file di disposizione" #: 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:417 msgid "Edit Images" msgstr "Modifica immagini" #: f.mashup.cc:418 f.mashup.cc:2432 msgid "Edit Text" msgstr "Modifica testi" #: f.mashup.cc:419 msgid "Edit Line" msgstr "Edit linea" #: f.mashup.cc:420 msgid "Rescale" msgstr "Riscala" #: f.mashup.cc:423 msgid "add or edit images" msgstr "Aggiungi o modifica immagini" #: f.mashup.cc:424 msgid "add or edit text" msgstr "Aggiungi o cambia testi" #: f.mashup.cc:425 msgid "add or edit lines/arrows" msgstr "Aggiungere o modificare linee/frecce" #: f.mashup.cc:426 msgid "change project scale" msgstr "cambia scala del progetto" #: f.mashup.cc:427 msgid "project complete" msgstr "Progetto completo" #: f.mashup.cc:428 msgid "cancel project" msgstr "Annulla progetto" #: f.mashup.cc:446 msgid "rescale project" msgstr "Riscala il progetto" #: f.mashup.cc:501 msgid "save Mashup output file" msgstr "Salva risultato del collage" #: f.mashup.cc:512 msgid "save Mashup project file?" msgstr "Salvo il progetto collage?" #: f.mashup.cc:524 msgid "delete Mashup project file?" msgstr "Elimino il progetto collage?" #: f.mashup.cc:583 msgid "Open Project" msgstr "Apri un progetto" #: f.mashup.cc:612 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "File immagine composizione mancante: \n" " %s" #: f.mashup.cc:669 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "File immagine di sfondo mancante: \n" " %s " #: f.mashup.cc:974 msgid "project file is defective" msgstr "Il file di progetto è difettoso" #: f.mashup.cc:1004 msgid "Save Project" msgstr "Salva il progetto" #: f.mashup.cc:1145 msgid "layout exceeds 2 gigabytes" msgstr "Il collage eccede 2 gigabyte" #: f.mashup.cc:1219 msgid "Click image to select, drag image to move." msgstr "Clicca immagine per selezionare, trascina per spostare." #: f.mashup.cc:1220 msgid "Make black margins transparent" msgstr "Rende trasparenti i margini neri" #: f.mashup.cc:1259 msgid "Current image:" msgstr "Immagine corrente: " #: f.mashup.cc:1263 msgid "Cycle through images:" msgstr "Cicla attraverso le immagini: " #: f.mashup.cc:1270 msgid "Scale" msgstr "Scala" #: f.mashup.cc:1279 msgid "Stacking Order" msgstr "Ordine di impilamento" #: f.mashup.cc:1280 msgid "raise" msgstr "Solleva" #: f.mashup.cc:1281 msgid "lower" msgstr "Abbassa" #: f.mashup.cc:1284 msgid "Base Transparency" msgstr "Trasparenza" #: f.mashup.cc:1288 msgid "Var. Transparency" msgstr "Trasparenza manuale" #: f.mashup.cc:1289 msgid "Paint" msgstr "Dipinto" #: f.mashup.cc:1292 msgid "Bend and fine-align" msgstr "Deforma e collima" #: f.mashup.cc:1293 msgid "Warp" msgstr "Deforma" #: f.mashup.cc:1302 msgid "Margins" msgstr "Margini" #: f.mashup.cc:1303 msgid "Hard" msgstr "Forte" #: f.mashup.cc:1304 f.repair.cc:5441 msgid "Blend" msgstr "Mescola" #: f.mashup.cc:1318 msgid "add images to layout" msgstr "Aggiunge immagini alla composizione" #: f.mashup.cc:1557 msgid "Paint Image Transparencies" msgstr "Dipingi aree trasparenti" #: f.mashup.cc:1575 msgid "Gradual" msgstr "Graduale" #: f.mashup.cc:1580 fotoxx.h:1148 msgid "Power" msgstr "Forza" #: f.mashup.cc:1814 msgid "Pull on the image with the mouse." msgstr "Trascina il mouse sull'immagine" #: f.mashup.cc:1830 msgid "Warp Image" msgstr "Deforma immagine" #: f.mashup.cc:2263 #, c-format msgid "exceeded %d images" msgstr "troppe immagini (massimo %d)" #: f.mashup.cc:2405 msgid "Enter text, [Add] to layout, edit properties." msgstr "Immetti il testo, [Aggiungi], modifica le opzioni" #: f.mashup.cc:2485 msgid "Text File:" msgstr "File di testo:" #: f.mashup.cc:2489 msgid "add entered text to layout" msgstr "Aggiunge testo alla composizione" #: f.mashup.cc:2618 msgid "click position to add text" msgstr "Clicca la posizione del testo" #: f.mashup.cc:2624 #, c-format msgid "exceeded %d text entries" msgstr "troppi testi (massimo %d)" #: f.mashup.cc:2747 msgid "Set line properties, [Add] to layout, edit." msgstr "Imposta opzioni linea, [Aggiungi], modifica" #: f.mashup.cc:2771 msgid "Edit Line/Arrow" msgstr "Modifica linea/freccia" #: f.mashup.cc:2826 msgid "add line/arrow to layout" msgstr "Aggiunge linea/freccia alla composizione" #: f.mashup.cc:2919 msgid "click position to add line" msgstr "Clicca la posizione della linea" #: f.mashup.cc:2925 #, c-format msgid "exceeded %d line entries" msgstr "Immesse più di %d linee" #: f.meta.cc:180 f.widgets.cc:669 msgid "View Metadata" msgstr "Mostra i metadati" #: f.meta.cc:430 f.meta.cc:1807 f.widgets.cc:412 f.widgets.cc:670 msgid "Edit Metadata" msgstr "Modifica metadati (normale)" #: f.meta.cc:433 f.meta.cc:3819 msgid "save metadata to file" msgstr "Salva i metadati in un file" #: f.meta.cc:442 msgid "Image Date" msgstr "Data immagine:" #: f.meta.cc:445 msgid "Time" msgstr "Ora" #: f.meta.cc:451 msgid "Rating (stars):" msgstr "Punteggio (stelle):" #: f.meta.cc:463 msgid "Caption" msgstr "Titolo" #: f.meta.cc:469 msgid "Comments" msgstr "Commenti" #: f.meta.cc:477 msgid "Enter New Tag" msgstr "Immetti nuova etichetta" #: f.meta.cc:484 f.meta.cc:5003 msgid "Matching Tags" msgstr "Etichette corrispondenti" #: f.meta.cc:492 msgid "Image Tags" msgstr "Etichette" #: f.meta.cc:498 msgid "Recent Tags" msgstr "Etichette recenti:" #: f.meta.cc:507 f.meta.cc:2029 f.meta.cc:5010 msgid "Defined Tags Category" msgstr "Categorie conosciute" #: f.meta.cc:898 msgid "date format is YYYY-MM-DD" msgstr "Il formato della data è AAAA-MM-GG" #: f.meta.cc:902 msgid "date is invalid" msgstr "la data non è valida" #: f.meta.cc:936 msgid "time format is HH:MM [:SS]" msgstr "Il formato dell'ora è HH:MM [:SS]" #: f.meta.cc:940 msgid "time is invalid" msgstr "L'ora non è valida" #: f.meta.cc:1025 fotoxx.h:1133 msgid "Manage Tags" msgstr "Gestione etichette" #: f.meta.cc:1025 msgid "orphan tags" msgstr "Etichette orfane" #: f.meta.cc:1029 msgid "category" msgstr "Categoria" #: f.meta.cc:1032 msgid "tag" msgstr "Etichetta" #: f.meta.cc:1039 msgid "Defined Tags:" msgstr "Etichette definite:" #: f.meta.cc:1811 f.meta.cc:2474 msgid "key name" msgstr "Nome chiave" #: f.meta.cc:1813 f.meta.cc:2475 msgid "key value" msgstr "Valore chiave" #: f.meta.cc:1900 f.widgets.cc:414 msgid "Delete Metadata" msgstr "Elimina metadati" #: f.meta.cc:1902 fotoxx.h:1082 msgid "All" msgstr "Tutto" #: f.meta.cc:1903 msgid "One Key:" msgstr "Una chiave:" #: f.meta.cc:2006 f.widgets.cc:415 f.widgets.cc:540 msgid "Batch Add/Remove Tags" msgstr "Aggiungi/elimina etichette in massa" #: f.meta.cc:2017 msgid "tags to add" msgstr "Etichette da aggiungere" #: f.meta.cc:2018 msgid "tags to remove" msgstr "Etichette da rimuovere" #: f.meta.cc:2102 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " troppe etichette" #: f.meta.cc:2217 msgid "specify files and tags" msgstr "specificare file ed etichette" #: f.meta.cc:2277 msgid "tag names file" msgstr "File di nomi di etichette" #: f.meta.cc:2349 #, 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:2461 msgid "Batch Metadata" msgstr "Metadati in massa" #: f.meta.cc:2468 msgid "Short List" msgstr "Lista breve" #: f.meta.cc:2469 msgid "Full List" msgstr "Lista completa" #: f.meta.cc:2511 msgid "enter key names" msgstr "Immetti i nomi delle etichette" #: f.meta.cc:2517 fotoxx.h:1141 msgid "no files selected" msgstr "Nessun file selezionato" #: f.meta.cc:2643 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:3276 f.meta.cc:4011 f.meta.cc:4277 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "Latitudine/Longitudine errati: %s %s" #: f.meta.cc:3320 f.meta.cc:3682 f.meta.cc:4486 f.meta.cc:4894 msgid "-noindex in use, disabled" msgstr "opzione '-noindex' in uso, funzione disabilitata" #: f.meta.cc:3336 msgid "choose map file" msgstr "Scegliere file di mappa" #: f.meta.cc:3444 msgid "" "fotoxx-maps package not installed \n" "(see http://kornelix.com/packages and /tarballs)" msgstr "" "Pacchetto fotoxx-maps non installato \n" "(http://kornelix.com/packages e /tarballs)" #: f.meta.cc:3449 #, c-format msgid "map file %s is missing" msgstr "File di mappa %s mancante" #: f.meta.cc:3453 #, 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:3731 msgid "No matching images found" msgstr "Nessuna immagine soddisfa la ricerca" #: f.meta.cc:3771 msgid "search range (km)" msgstr "distanza (km)" #: f.meta.cc:3804 f.widgets.cc:418 f.widgets.cc:671 msgid "Edit Geotags" msgstr "Modifica i Geotags" #: f.meta.cc:3805 msgid "Geocoding web service courtesy of" msgstr "Il servizio web Geocoding è cortesia di" #: f.meta.cc:3821 f.meta.cc:4060 f.meta.cc:5783 msgid "city" msgstr "Città" #: f.meta.cc:3824 f.meta.cc:4063 f.meta.cc:5786 msgid "country" msgstr "Nazione" #: f.meta.cc:3906 f.meta.cc:4194 msgid "city not found" msgstr "Città non trovata" #: f.meta.cc:4027 f.widgets.cc:419 f.widgets.cc:543 msgid "Batch Add Geotags" msgstr "Aggiunta Geotags in blocco" #: f.meta.cc:4256 msgid "" "data is incomplete \n" " proceed?" msgstr "" "I dati sono incompleti \n" " proseguire?" #: f.meta.cc:4313 msgid "choose city" msgstr "Scegli città" #: f.meta.cc:4400 msgid "not found" msgstr "non trovata" #: f.meta.cc:4401 msgid "city and country required" msgstr "Indicare città e nazione" #: f.meta.cc:4501 msgid "Report Geotag Groups" msgstr "Visualizza gruppi Geotags" #: f.meta.cc:4502 msgid "Group by country" msgstr "Raggruppa per nazione" #: f.meta.cc:4503 msgid "Group by country/city" msgstr "Raggruppa per nazione/città" #: f.meta.cc:4504 msgid "Group by country/city/date" msgstr "Raggruppa per nazione/città/data" #: f.meta.cc:4507 msgid "Combine within" msgstr "Combina entro" #: f.meta.cc:4509 msgid "days" msgstr "giorni" #: f.meta.cc:4625 msgid "geotag groups" msgstr "Gruppi geotag" #: f.meta.cc:4931 msgid "Search Image Metadata" msgstr "Cerca nei metadati dell'immagine" #: f.meta.cc:4935 msgid "images to search:" msgstr "immagini da cercare:" #: f.meta.cc:4936 msgid "all" msgstr "tutte" #: f.meta.cc:4937 msgid "current set only" msgstr "insieme corrente" #: f.meta.cc:4940 msgid "matching images:" msgstr "immagini corrispondenti:" #: f.meta.cc:4941 msgid "new set" msgstr "nuovo insieme" #: f.meta.cc:4942 msgid "add to set" msgstr "aggiungi all'insieme" #: f.meta.cc:4943 msgid "remove" msgstr "rimuovi" #: f.meta.cc:4946 msgid "report type:" msgstr "Tipo di risultato:" #: f.meta.cc:4947 msgid "gallery" msgstr "galleria" #: f.meta.cc:4948 fotoxx-15.11.cc:791 msgid "metadata" msgstr "metadati" #: f.meta.cc:4954 msgid "date range" msgstr "Intervallo date: " #: f.meta.cc:4955 msgid "stars range" msgstr "Voto da/a:" #: f.meta.cc:4956 msgid "search tags" msgstr "Cerca etichette" #: f.meta.cc:4957 msgid "search text" msgstr "Cerca testo" #: f.meta.cc:4958 msgid "search files" msgstr "Cerca nei file" #: f.meta.cc:4963 msgid "(yyyymmdd)" msgstr "(aaaammgg)" #: f.meta.cc:4968 msgid "last version only" msgstr "Solo ultime versioni" #: f.meta.cc:4970 msgid "all/any" msgstr "Tutte/Qualsiasi" #: f.meta.cc:4989 msgid "other criteria" msgstr "altri criteri" #: f.meta.cc:4993 msgid "other" msgstr "altro" #: f.meta.cc:4999 msgid "Enter Search Tag" msgstr "Immetti etichette di ricerca" #: f.meta.cc:5294 #, c-format msgid "not a defined tag: %s" msgstr "Etichetta sconosciuta: %s" #: f.meta.cc:5325 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:5332 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:5376 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "Date di ricerca non ragionevoli \n" " %s %s" #: f.meta.cc:5394 msgid "stars range not reasonable" msgstr "punteggio (stelle) non ammissibile" #: f.meta.cc:5639 #, c-format msgid "images added: %d removed: %d new count: %d" msgstr "Immagini aggiunte: %d; tolte: %d; totale corrente: %d" #: f.meta.cc:5642 msgid "no changes made" msgstr "nessun cambiamento fatto" #: f.meta.cc:5780 msgid "Add Geotags Search Criteria" msgstr "Aggiungi criteri di ricerca Geotags" #: f.meta.cc:5799 msgid "range (km)" msgstr "distanza (km)" #: f.meta.cc:5892 msgid "error in latitude/longitude/range" msgstr "errore nella specifica di latitudine/longitudine/distanza" #: f.meta.cc:5956 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:5980 msgid "Additional Items for Report" msgstr "Informazioni addizionali da riportare" #: f.meta.cc:5986 msgid "Keyword" msgstr "parola chiave" #: f.meta.cc:5993 msgid "Match Criteria" msgstr "criterio di corrispondenza" #: f.meta.cc:6553 msgid "image index is missing" msgstr "Indice immagini mancante" #: f.repair.cc:1041 msgid "Apply repeatedly while watching the image." msgstr "Applica ripetutamente osservando l'immagine" #: f.repair.cc:1042 msgid "Measure" msgstr "Misura" #: f.repair.cc:1078 msgid "Noise Reduction" msgstr "Riduzione disturbo" #: f.repair.cc:1099 msgid "Flatten 1" msgstr "Appiattimento 1" #: f.repair.cc:1100 msgid "Flatten 2" msgstr "Appiattimento 2" #: f.repair.cc:1101 msgid "Median" msgstr "Filtro mediano" #: f.repair.cc:1121 msgid "dark areas" msgstr "zone scure" #: f.repair.cc:1123 msgid "all areas" msgstr "tutte le zone" #: f.repair.cc:1246 msgid "Measure Noise" msgstr "Misura disturbo" #: f.repair.cc:1247 msgid "Click on a monotone image area." msgstr "Cliccare una zona omogenea" #: f.repair.cc:2177 msgid "" "1. Drag mouse to select. \n" "2. Erase. 3. Repeat. " msgstr "" "1. Trascina per selezionare. \n" "2. Cancella. 3. Ripeti. " #: f.repair.cc:2199 f.widgets.cc:457 msgid "Smart Erase" msgstr "Cancellazione intelligente" #: f.repair.cc:2204 fotoxx.h:1152 msgid "Radius" msgstr "Raggio" #: f.repair.cc:2206 f.widgets.cc:455 msgid "Blur" msgstr "Sfuoca" #: f.repair.cc:2209 msgid "New Area" msgstr "Nuova area" #: f.repair.cc:2558 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:2574 msgid "Red Eye Reduction" msgstr "Riduzione occhi rossi" #: f.repair.cc:3038 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" #: f.repair.cc:3070 f.widgets.cc:459 msgid "Paint/Clone" msgstr "Dipingi pixel" #: f.repair.cc:3074 msgid "paint color" msgstr "Colore pennello" #: f.repair.cc:3077 msgid "copy from image" msgstr "Copia dall'immagine" #: f.repair.cc:3084 f.repair.cc:3641 msgid "paintbrush radius" msgstr "Raggio del pennello:" #: f.repair.cc:3085 msgid "transparency center" msgstr "Trasparenza al centro:" #: f.repair.cc:3086 msgid "transparency edge" msgstr "Trasparenza al bordo:" #: f.repair.cc:3093 f.repair.cc:3648 msgid "gradual paint" msgstr "pittura graduale" #: f.repair.cc:3100 #, c-format msgid "Undo Memory %d%c" msgstr "Memoria per Annulla %d%c" #: f.repair.cc:3436 msgid "" "Undo memory limit has been reached. \n" "Save work with [done], then resume painting." msgstr "" "La memoria per Annulla è finita. \n" "Salva con [Fatto], poi riprendi il lavoro." #: f.repair.cc:3601 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:3633 f.widgets.cc:460 msgid "Paint Transparency" msgstr "Dipingi trasparenza" #: f.repair.cc:3642 msgid "strength center" msgstr "Rafforza il centro" #: f.repair.cc:3643 msgid "strength edge" msgstr "Rafforza lati" #: f.repair.cc:3858 f.widgets.cc:461 msgid "Color Mode" msgstr "Modo di colore" #: f.repair.cc:3862 msgid "black/white positive" msgstr "Bianco e nero, positivo" #: f.repair.cc:3863 msgid "black/white negative" msgstr "Bianco e nero, negativo" #: f.repair.cc:3864 msgid "color positive" msgstr "A colori, positivo" #: f.repair.cc:3865 msgid "color negative" msgstr "A colori, negativo" #: f.repair.cc:3866 msgid "sepia" msgstr "Seppia" #: f.repair.cc:4046 f.widgets.cc:462 msgid "Shift Colors" msgstr "Modula componenti" #: f.repair.cc:4317 msgid "+Brightness" msgstr "+Luminosità" #: f.repair.cc:4318 msgid "+Red -Cyan" msgstr "+Rosso -Ciano" #: f.repair.cc:4319 msgid "+Green -Magenta" msgstr "+Verde -Magenta" #: f.repair.cc:4320 msgid "+Blue -Yellow" msgstr "+Blu -Giallo" #: f.repair.cc:4326 fotoxx.h:1154 msgid "Red" msgstr "Rosso" #: f.repair.cc:4327 fotoxx.h:1120 msgid "Green" msgstr "Verde" #: f.repair.cc:4328 fotoxx.h:1089 msgid "Blue" msgstr "Blu" #: f.repair.cc:4623 msgid "Color to change" msgstr "" #: f.repair.cc:4625 msgid "shift+click on image to select color" msgstr "" #: f.repair.cc:4638 msgid "Color Hue" msgstr "Tonalità colore" #: f.repair.cc:4639 msgid "Saturation" msgstr "Saturazione" #: f.repair.cc:4640 msgid "Lightness" msgstr "Lucentezza" #: f.repair.cc:4641 msgid "Color Match" msgstr "" #: f.repair.cc:4642 msgid "Adjustment" msgstr "Regolazione" #: f.repair.cc:5028 msgid "Ramp brightness across image" msgstr "Rampe di luce" #: f.repair.cc:5352 f.tools.cc:2048 msgid "Click image to select pixels." msgstr "Clicca l'immagine per selezionare i pixel" #: f.repair.cc:5383 f.widgets.cc:466 msgid "Color Ramp" msgstr "Rampa di colore" #: f.repair.cc:5394 msgid "Metric:" msgstr "Metrica" #: f.repair.cc:5817 msgid "Color Match Images" msgstr "Allinea colori" #: f.repair.cc:5843 msgid "mouse radius for color sample" msgstr "Raggio di prelievo del colore" #: f.repair.cc:5846 msgid "image for source color" msgstr "l'immagine di riferimento" #: f.repair.cc:5848 msgid "click on image to get source color" msgstr "Clicca nell'immagine per prelevare il colore" #: f.repair.cc:5851 msgid "image to set matching color" msgstr "l'immagine a cui impostare il colore" #: f.repair.cc:5853 msgid "click on image to set matching color" msgstr "Clicca nell'immagine per allineare il colore" #: f.repair.cc:5914 msgid "select source image color first" msgstr "Seleziona prima l'immagine di riferimento" #: f.repair.cc:6099 msgid "Change Color Profile" msgstr "Cambia profilo di colore" #: f.repair.cc:6103 msgid "input profile" msgstr "Profilo in ingresso" #: f.repair.cc:6107 msgid "output profile" msgstr "Profilo in uscita" #: f.repair.cc:6123 msgid "color profile" msgstr "Profilo di colore" #: f.repair.cc:6169 f.repair.cc:6175 #, c-format msgid "unknown cms profile %s" msgstr "Profilo CMS sconosciuto %s" #: f.repair.cc:6307 f.widgets.cc:469 msgid "Remove Dust" msgstr "Rimozione polvere" #: f.repair.cc:6311 msgid "spot size limit" msgstr "Raggio max della polvere" #: f.repair.cc:6314 msgid "max. brightness" msgstr "Luminosità massima" #: f.repair.cc:6317 msgid "min. contrast" msgstr "Contrasto minimo" #: f.repair.cc:7098 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:7121 f.widgets.cc:471 msgid "Color Fringes" msgstr "Frange di colore" #: f.repair.cc:7320 f.widgets.cc:472 msgid "Stuck Pixels" msgstr "Pixel rovinati (incollati)" #: f.repair.cc:7326 msgid "pixel group" msgstr "Gruppo di pixel:" #: f.repair.cc:7334 f.repair.cc:7402 msgid "stuck pixels:" msgstr "Pixel difettosi:" #: f.repair.cc:7430 msgid "Load Stuck Pixels" msgstr "Carica file pixel difettosi" #: f.repair.cc:7432 msgid "File:" msgstr "File:" #: f.repair.cc:7457 f.repair.cc:7514 msgid "Stuck Pixels file" msgstr "File di pixel difettosi" #: f.repair.cc:7494 msgid "file format error" msgstr "Formato file errato" #: f.tools.cc:70 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:72 msgid "Select to add, click on X to delete." msgstr "Seleziona per aggiungere, clicca X per eliminare." #: f.tools.cc:73 msgid "Select directory for thumbnails." msgstr "Seleziona directory per miniature" #: f.tools.cc:74 msgid "" "Index function terminated. Fotoxx will close. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Funzione indice terminata. Fotoxx terminerà. \n" "L'indice è richiesto per le ricerche e le mappe \n" "e per generare le gallerie in modo rapido." #: f.tools.cc:106 f.widgets.cc:513 msgid "Index Image Files" msgstr "Gestione file indice" #: f.tools.cc:250 msgid "Choose top image directories" msgstr "Scegli la cartella principale delle immagini" #: f.tools.cc:251 msgid "Choose thumbnail directory" msgstr "Scegliere directory miniature" #: f.tools.cc:324 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:330 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" "Directory non valida: \n" " %s \n" "Si prega di rimuoverla." #: f.tools.cc:333 #, 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:336 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" "Directory duplicata: \n" " %s \n" "Si prega di rimuoverla." #: f.tools.cc:385 msgid "no thumbnails directory defined" msgstr "nessuna directory miniature definita" #: f.tools.cc:735 msgid "COMPLETED" msgstr "COMPLETATO" #: f.tools.cc:776 msgid "Indexing is required for first-time startup." msgstr "L'indicizzazione è richiesta per il primo avviamento." #: f.tools.cc:838 msgid "Recent Files Gallery" msgstr "Galleria file recenti" #: f.tools.cc:839 msgid "Newest Files Gallery" msgstr "Galleria delle immagini nuove" #: f.tools.cc:840 msgid "Previous Gallery" msgstr "Galleria precedente" #: f.tools.cc:841 msgid "Previous Image" msgstr "Immagine precedente" #: f.tools.cc:842 msgid "Blank Window" msgstr "Finestra vuota" #: f.tools.cc:843 msgid "Directory" msgstr "Directory" #: f.tools.cc:844 msgid "Image File" msgstr "File immagine" #: f.tools.cc:887 f.widgets.cc:514 msgid "User Options" msgstr "Opzioni d'uso" #: f.tools.cc:890 msgid "Startup Display" msgstr "Vista iniziale" #: f.tools.cc:901 msgid "Menu Style" msgstr "Stile del menù" #: f.tools.cc:902 msgid "Icons" msgstr "Icone" #: f.tools.cc:903 msgid "Icons + Text" msgstr "Icone e testo" #: f.tools.cc:905 msgid "Icon size" msgstr "Dimensione icone" #: f.tools.cc:909 msgid "Dialog font and size" msgstr "Impostazione font e dimensione" #: f.tools.cc:914 msgid "Image Pan/scroll:" msgstr "Scorrimento immagine a video:" #: f.tools.cc:917 msgid "Zooms for 2x" msgstr "Zoom per 2x" #: f.tools.cc:926 msgid "JPEG save quality" msgstr "Qualità Jpeg" #: f.tools.cc:933 msgid "Curve node capture distance" msgstr "Distanza di cattura dei nodi della curva" #: f.tools.cc:937 msgid "show hidden directories in gallery view" msgstr "Mostra cartelle nascoste in Vista galleria" #: f.tools.cc:940 msgid "prev/next shows last file version only" msgstr "Prec./Pross. della barra mostrano solo l'ultima versione" #: f.tools.cc:943 msgid "RAW command" msgstr "Comando RAW" #: f.tools.cc:947 msgid "RAW file types" msgstr "Tipi di file RAW" #: f.tools.cc:1029 msgid "Delete present thumbnails to make effective" msgstr "Eliminare le miniature presenti per rendere effettivo" #: f.tools.cc:1051 msgid "Select startup directory" msgstr "Seleziona cartella d'avvio" #: f.tools.cc:1058 msgid "Select startup image file" msgstr "Seleziona file d'avvio" #: f.tools.cc:1096 msgid "startup directory is invalid" msgstr "Cartella iniziale non valida" #: f.tools.cc:1107 msgid "startup file is invalid" msgstr "File iniziale non valido" #: f.tools.cc:1247 msgid "Edit KB Shortcuts" msgstr "Gestisci le scorciatoie di tastiera" #: f.tools.cc:1252 msgid "shortcut key:" msgstr "Scorciatoia:" #: f.tools.cc:1253 msgid "(enter key)" msgstr "(premere il/i tasti)" #: f.tools.cc:1379 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" è riservata - non si può usare" #: f.tools.cc:1533 msgid "unable to save KB-shortcuts file" msgstr "Non riesco a salvare le scorciatoie" #: f.tools.cc:1837 f.widgets.cc:517 msgid "Grid Lines" msgstr "Linee griglia" #: f.tools.cc:1846 msgid "x-spacing" msgstr "Passo X:" #: f.tools.cc:1847 msgid "x-count" msgstr "Num. barre X:" #: f.tools.cc:1848 msgid "x-enable" msgstr "Abilita X:" #: f.tools.cc:1854 msgid "y-spacing" msgstr "Passo Y:" #: f.tools.cc:1855 msgid "y-count" msgstr "Num. righe Y:" #: f.tools.cc:1856 msgid "y-enable" msgstr "Abilita Y:" #: f.tools.cc:1976 f.tools.cc:1981 f.widgets.cc:518 msgid "Line Color" msgstr "Colore della linea" #: f.tools.cc:1978 msgid "Area Color" msgstr "Colore dell'area" #: f.tools.cc:2083 f.widgets.cc:519 msgid "Show RGB" msgstr "Esplora componenti RGB" #: f.tools.cc:2375 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key M to toggle dialog." msgstr "" "Trascinare il mouse nell'immagine. \n" "Cliccare per annullare. \n" "Tasto M per mostrare/nascondere la finestra di dialogo" #: f.tools.cc:2401 f.widgets.cc:520 msgid "Magnify Image" msgstr "Lente d'ingrandimento" #: f.tools.cc:2410 msgid "X-size" msgstr "Ingrandimento" #: f.tools.cc:2655 msgid "Darkest and Brightest Pixels" msgstr "Cerca pixel scuri e chiari" #: f.tools.cc:2678 msgid "Dark Limit" msgstr "Più scuri di..." #: f.tools.cc:2679 msgid "Bright Limit" msgstr "Più chiari di..." #: f.tools.cc:2774 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:2870 f.widgets.cc:523 msgid "Monitor Gamma" msgstr "Gamma dello schermo" #: f.tools.cc:2930 msgid "Available Translations" msgstr "Traduzioni disponibili" #: f.tools.cc:2934 msgid "Set Language" msgstr "Cambia lingua" #: f.tools.cc:3071 f.widgets.cc:526 msgid "Calibrate Printer" msgstr "Calibrazione stampante" #: f.tools.cc:3096 msgid "print color chart" msgstr "Stampa l'immagine campione (mappa dei colori)" #: f.tools.cc:3097 msgid "scan and save color chart" msgstr "Acquisisci con lo scanner l'immagine stampata e salvala in un file" #: f.tools.cc:3098 msgid "align and trim color chart" msgstr "Raddrizza e ritaglia l'immagine acquisita" #: f.tools.cc:3099 msgid "open and process color chart" msgstr "Apri e processa l'immagine stampata, acquisita e regolata" #: f.tools.cc:3100 msgid "print image with revised colors" msgstr "Stampa la mappa dei colori con le correzioni attive" #: f.tools.cc:3264 #, c-format msgid "" "Scan the printed color chart. \n" "Save in %s/" msgstr "" "Acquisire con lo scanner la mappa dei colori stampata. \n" "Salvare in %s/" #: f.tools.cc:3278 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation caused by the scanner. \n" "Trim off all margins. Be careful not to cut off \n" "any color tile edges or leave any white margins." msgstr "" "Apri e correggi la mappa dei colori stampata e acquisita. \n" "Rimuovi deformazioni e rotazioni causate dallo scanner. \n" "Elimina i margini bianchi; attenzione a non eliminare \n" "le strisce colorate o loro parti." #: f.tools.cc:3310 msgid "Open the trimmed color chart file" msgstr "Apri il file con l'immagine acquisita e ritagliata" #: f.tools.cc:3443 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:3483 msgid "Color map file to use" msgstr "File della mappa colori da usare" #: f.tools.cc:3500 msgid "Select the image file to print." msgstr "Selezionare l'immagine da stampare." #: f.tools.cc:3565 msgid "converting colors..." msgstr "Conversione colori..." #: f.tools.cc:3665 msgid "Image colors are converted for printing." msgstr "Regolazione dei colori per la stampa." #: f.widgets.cc:111 msgid "Album" msgstr "Album" #: f.widgets.cc:113 msgid "TOP" msgstr "CIMA" #: f.widgets.cc:165 msgid "Current Image File (key F)" msgstr "Immagine corrente (tasto F)" #: f.widgets.cc:166 msgid "Thumbnail Gallery (key G)" msgstr "Galleria miniature (tasto G)" #: f.widgets.cc:167 msgid "World Maps (key W)" msgstr "Mappa del mondo (tasto W)" #: f.widgets.cc:170 msgid "Favorite Functions" msgstr "Funzioni preferite" #: f.widgets.cc:171 msgid "File: Open, RAW, Rename, Trash, Print" msgstr "File: apri, RAW, Rinomina, Cestina, Stampa" #: f.widgets.cc:172 msgid "Save modified image file to disk" msgstr "Salva su disco l'immagine modificata" #: f.widgets.cc:173 msgid "Open previous or next file (left/right mouse click)" msgstr "Aprire il file precedente o successivo (clic sinistro/destro)" #: f.widgets.cc:174 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "Metadati: Titoli, Etichette, Punteggi, Geotags, Ricerca..." #: f.widgets.cc:175 msgid "Areas: Select areas to edit, copy and paste" msgstr "Aree: selezione aree per edit, copia e incolla" #: f.widgets.cc:176 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "Edit: Ritaglia, Ruota, Ridimensiona, Luminosità, Contrasto, Scritte..." #: f.widgets.cc:177 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "Ripara: Nitidezza, Disturbo, Occhi rossi, Colore, Dipinto, Clona..." #: f.widgets.cc:178 msgid "Bend: Fix Perspective, Bend/Warp image ..." msgstr "Deforma: Correggi prospettiva, Raddrizza/Distorce..." #: f.widgets.cc:179 msgid "Effects: Special Effects, Arty Transforms" msgstr "Effetti: effetti speciali, trasformazioni artistiche" #: f.widgets.cc:180 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "Combina: HDR, HDF, Panorama, Impila, Componi" #: f.widgets.cc:181 msgid "" "Undo or Redo one edit (left/right mouse click) \n" " hold key A to include all edits" msgstr "" "Annulla o ripeti un'operazione (clic sinistro/destro)\n" "Tenere premuto il tasto 'A' per agire su tutte le modifiche" #: f.widgets.cc:183 msgid "Tools: Index, Options, Shortcuts, Magnify ..." msgstr "Strumenti: Indici, Opzioni, Scorciatoie, Lente, ..." #: f.widgets.cc:184 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "Aiuto: Guida rapida, Manuale utente, Ultime novità..." #: f.widgets.cc:187 msgid "Set gallery from current image file" msgstr "Imposta la galleria dall'immagine corrente" #: f.widgets.cc:188 msgid "Albums: Manage Albums, Slide Show, Desktop Wallpaper" msgstr "" #: f.widgets.cc:189 msgid "go to bookmarked image" msgstr "Va a un segnalibro" #: f.widgets.cc:190 msgid "increase thumbnail size" msgstr "Aumenta la dimensione delle miniature" #: f.widgets.cc:191 msgid "reduce thumbnail size" msgstr "Riduci la dimensione delle miniature" #: f.widgets.cc:192 msgid "change sort order" msgstr "Cambia ordinamento" #: f.widgets.cc:193 msgid "jump to beginning (top)" msgstr "Salta alla cima" #: f.widgets.cc:194 msgid "jump to end (bottom)" msgstr "Salta al fondo" #: f.widgets.cc:195 msgid "previous page" msgstr "Pagina precedente" #: f.widgets.cc:196 msgid "next page" msgstr "Pagina successiva" #: f.widgets.cc:197 msgid "slow scroll" msgstr "Scorrimento lento" #: f.widgets.cc:198 msgid "Batch conversion, metadata, RAW processing" msgstr "Conversioni in massa, metadati, file RAW" #: f.widgets.cc:201 msgid "Choose a map for image locations" msgstr "Scegliere una mappa per i luoghi delle immagini" #: f.widgets.cc:202 msgid "Set image search radius for map click" msgstr "Imposta raggio di ricerca per clic su mappa" #: f.widgets.cc:205 msgid "Open another window" msgstr "Apri altra finestra" #: f.widgets.cc:206 msgid "Open a new image file" msgstr "Apri un ulteriore file" #: f.widgets.cc:207 msgid "Open the previously seen file" msgstr "Apri il file precedente" #: f.widgets.cc:208 msgid "Open a recently seen file" msgstr "Apri un file recente" #: f.widgets.cc:209 msgid "Open a newly added file" msgstr "Apri una immagine aggiunta" #: f.widgets.cc:210 f.widgets.cc:211 msgid "Open and edit a camera RAW file" msgstr "Apri un file RAW" #: f.widgets.cc:212 msgid "Change the image file name" msgstr "Rinomina il file" #: f.widgets.cc:213 msgid "Create a blank image" msgstr "Crea immagine nuova" #: f.widgets.cc:214 msgid "Move image file to Trash" msgstr "Sposta il file nel cestino" #: f.widgets.cc:215 msgid "Permanently delete image file" msgstr "Elimina permanentemente il file" #: f.widgets.cc:216 msgid "Print the current image" msgstr "Stampa l'immagine corrente" #: f.widgets.cc:217 msgid "Print current image with adjusted colors" msgstr "Stampa l'immagine corrente con i colori corretti" #: f.widgets.cc:218 f.widgets.cc:406 msgid "Quit Fotoxx" msgstr "Esci da Fotoxx" #: f.widgets.cc:221 msgid "List a few key metadata items" msgstr "Mostra alcuni metadati di base" #: f.widgets.cc:222 msgid "List all metadata items" msgstr "Mostra tutti i metadati" #: f.widgets.cc:223 msgid "(Toggle) show captions and comments" msgstr "Titoli e commenti sulla foto" #: f.widgets.cc:224 msgid "Edit image tags/caption/rating ..." msgstr "Modifica etichette/titolo/votazione..." #: f.widgets.cc:225 msgid "Edit any image metadata" msgstr "Modifica qualsiasi metadato dell'immagine" #: f.widgets.cc:226 msgid "Remove all metadata from an image" msgstr "Rimuove tutti i metadati da un'immagine" #: f.widgets.cc:227 msgid "Add/remove tags for multiple images" msgstr "Aggiungi/togli etichette in massa" #: f.widgets.cc:228 msgid "Convert tag names for all images" msgstr "Converte nomi etichette per tutte le immagini" #: f.widgets.cc:229 msgid "Add/change/delete metadata for multiple images" msgstr "Aggiunge/cambia/elimina metadati in diverse immagini " #: f.widgets.cc:230 msgid "Edit image location and geotags" msgstr "Modifica luogo e Geotags dell'immagine" #: f.widgets.cc:231 msgid "Add/revise geotags for multiple images" msgstr "Aggiungi/modifica geotag per diverse immagini" #: f.widgets.cc:232 msgid "Find all images for a location [date]" msgstr "Cerca tutte le immagini di un luogo/data" #: f.widgets.cc:233 msgid "Find images meeting select criteria" msgstr "Cerca immagini con determinati criteri" #: f.widgets.cc:236 msgid "Select object or area for editing" msgstr "Seleziona oggetto o area da modificare" #: f.widgets.cc:237 msgid "Show (outline) existing area" msgstr "Mostra il contorno dell'area definita" #: f.widgets.cc:238 msgid "Hide existing area" msgstr "Nascondi l'area definita" #: f.widgets.cc:239 msgid "Enable area for editing" msgstr "Abilita area alle modifiche" #: f.widgets.cc:240 msgid "Disable area for editing" msgstr "Disabilita area per modifiche" #: f.widgets.cc:241 msgid "Reverse existing area" msgstr "Inverti l'area definita" #: f.widgets.cc:242 msgid "Erase existing area" msgstr "Cancella area" #: f.widgets.cc:243 msgid "Copy area for later pasting into image" msgstr "Copia area per incollarla poi in un'immagine" #: f.widgets.cc:244 msgid "Paste previously copied area into image" msgstr "Incolla nell'immagine l'area precedentemente copiata" #: f.widgets.cc:245 msgid "Open a file and paste as area into image" msgstr "Apre un file e incolla come area" #: f.widgets.cc:246 msgid "Save area to a file with transparency" msgstr "Salva area in un file con trasparenza" #: f.widgets.cc:249 msgid "Trim/Crop margins and/or Rotate" msgstr "Regola/ritaglia margini e/o ruota" #: f.widgets.cc:250 msgid "Upright a rotated image" msgstr "Raddrizza un'immagine (se ruotata)" #: f.widgets.cc:251 f.widgets.cc:252 msgid "Fast auto enhance that may work OK" msgstr "Auto correzione che potrebbe anche funzionare bene..." #: f.widgets.cc:253 msgid "Adjust brightness, contrast, color" msgstr "Correggi luminosità contrasto e colore" #: f.widgets.cc:254 msgid "Add local contrast, enhance details" msgstr "Aggiungi contrasto locale (più dettagli)" #: f.widgets.cc:255 msgid "Adjust brightness distribution" msgstr "Regola distribuzione della luminosità" #: f.widgets.cc:256 msgid "Flatten zonal brightness distribution" msgstr "Regola luminosità a zone" #: f.widgets.cc:257 msgid "Change pixel dimensions" msgstr "Modifica le dimensioni in pixel" #: f.widgets.cc:258 msgid "Mirror image horizontally or vertically" msgstr "Rispecchia in orizzontale o verticale" #: f.widgets.cc:259 msgid "Write text on image" msgstr "Disegna scritte sull'immagine" #: f.widgets.cc:260 msgid "Write lines or arrows on image" msgstr "Aggiunge linee o frecce sull'immagine" #: f.widgets.cc:261 msgid "Fix brightness uniformity across image" msgstr "Imposta l'uniformità della luce lungo l'immagine" #: f.widgets.cc:262 msgid "Paint edit function gradually with mouse" msgstr "Applica una funzione gradualmente col mouse" #: f.widgets.cc:263 msgid "Leverage edits by brightness or color" msgstr "Modula la funzione corrente secondo luminosità/colore" #: f.widgets.cc:264 msgid "Edit plugins menu or run a plugin function" msgstr "Modifica menu plugin o invoca una funzione" #: f.widgets.cc:267 msgid "Make the image look sharper" msgstr "Produci un'immagine più dettagliata" #: f.widgets.cc:268 msgid "Make the image look fuzzy" msgstr "Produci un'immagine più imprecisa" #: f.widgets.cc:269 msgid "Filter noise from low-light photos" msgstr "Elimina disturbi da foto con poca luce" #: f.widgets.cc:270 msgid "Remove unwanted objects" msgstr "Elimina oggetti non voluti" #: f.widgets.cc:271 msgid "Fix red-eyes from electronic flash" msgstr "Correggi gli occhi rossi a causa del flash" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Disegna pixel con il mouse" #: f.widgets.cc:273 msgid "Paint image transparency using the mouse" msgstr "Pennella la trasparenza con il mouse" #: f.widgets.cc:274 msgid "Make BW/color, negative/positive, sepia" msgstr "Trasforma in B/N o a colori, negativo, seppia..." #: f.widgets.cc:275 msgid "Shift/convert colors into other colors" msgstr "Regola i singoli componenti del colore" #: f.widgets.cc:276 msgid "Adjust color using RGB or CMY colors" msgstr "Regola colori usando RGB o CMY" #: f.widgets.cc:277 msgid "Adjust color using HSL colors" msgstr "Regola colori usando HSL" #: f.widgets.cc:278 msgid "Adjust color in selected image areas" msgstr "Regola il colore in aree selezionate" #: f.widgets.cc:279 msgid "Match colors on one image with another" msgstr "Allinea il colore di un'immagine con un'altra" #: f.widgets.cc:280 msgid "Convert to another color profile" msgstr "Converte a un altro profilo di colore" #: f.widgets.cc:281 msgid "Remove dust spots from scanned slides" msgstr "Elimina le macchie di polvere da diapositive scansionate" #: f.widgets.cc:282 msgid "Smoothen edges with jaggies" msgstr "Sfuoca bordi sgranati" #: f.widgets.cc:283 msgid "Reduce Chromatic Abberation" msgstr "Corregge le barre colorate estranee alla foto" #: f.widgets.cc:284 msgid "Erase known hot and dark pixels" msgstr "" "Cancella pixel difettosi della fotocamera, detti Hot-caldi o Dark-scuri " #: f.widgets.cc:287 msgid "Remove curvature, esp. panoramas" msgstr "Elimina la curvatura, specialmente da panorama" #: f.widgets.cc:288 msgid "Straighten objects seen from an angle" msgstr "Raddrizza oggetti visti da un angolo" #: f.widgets.cc:289 msgid "Distort image areas using the mouse" msgstr "Distorci un'area usando il mouse" #: f.widgets.cc:290 f.widgets.cc:291 f.widgets.cc:292 msgid "Distort the whole image using the mouse" msgstr "Distorci l'intera foto usando il mouse" #: f.widgets.cc:293 msgid "Flatten a photographed book page" msgstr "Appiattisce la foto della pagina di un libro" #: f.widgets.cc:296 msgid "Reduce color depth (posterize)" msgstr "Riduce la risoluzione dei colori (poster)" #: f.widgets.cc:297 msgid "Convert to pencil sketch" msgstr "Converte in disegno a matita" #: f.widgets.cc:298 msgid "Convert to line drawing (edge detection)" msgstr "Converte in disegno a linee (riconoscimento bordi)" #: f.widgets.cc:299 msgid "Convert to solid color drawing" msgstr "Converte in disegno con colori piatti" #: f.widgets.cc:300 msgid "Graduated Blur depending on contrast" msgstr "Sfumatura graduale basata sul contrasto" #: f.widgets.cc:301 msgid "Create an embossed or 3D appearance" msgstr "Crea un effetto scolpito o 3D" #: f.widgets.cc:302 msgid "Convert to square tiles" msgstr "Crea un effetto piastrellato" #: f.widgets.cc:303 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "Crea un effetto puntinato (Roy Lichtenstein)" #: f.widgets.cc:304 msgid "Convert into a simulated painting" msgstr "Crea un effetto di dipinto a olio" #: f.widgets.cc:305 msgid "Change brightness or color radially" msgstr "Cambia luminosità o contrasto in modo radiale" #: f.widgets.cc:306 msgid "Add texture to an image" msgstr "Aggiungi un motivo (texture)" #: f.widgets.cc:307 msgid "Tile image with a repeating pattern" msgstr "Sovraimprime all'immagine un motivo ripetuto" #: f.widgets.cc:308 msgid "Create a mosaic with tiles made from all images" msgstr "Crea un mosaico con tessere da tutte le immagini" #: f.widgets.cc:309 msgid "Process an image using a custom kernel" msgstr "Elabora un'immagine applicando una matrice utente" #: f.widgets.cc:310 msgid "Warp an image with a wave pattern" msgstr "Distorce immagine con motivo di onde" #: f.widgets.cc:311 msgid "Blur an image in the direction of mouse drags" msgstr "Sfuma un'immagine in direzione dei trascinamenti col mouse" #: f.widgets.cc:312 msgid "Make a spherical projection of an image" msgstr "" #: f.widgets.cc:315 msgid "Combine bright/dark images for better detail" msgstr "Combina immagini chiare/scure per guadagnare in dettaglio" #: f.widgets.cc:316 msgid "Combine near/far focus images for deeper focus" msgstr "Combina immagini con profondità diverse per ampliare la messa a fuoco" #: f.widgets.cc:317 msgid "Combine images to erase passing people, etc." msgstr "Combina immagini per eliminare oggetti (es. passanti, automobili...)" #: f.widgets.cc:318 msgid "Combine noisy images into a low-noise image" msgstr "Combina immagini disturbate in una con minor disturbo" #: f.widgets.cc:319 msgid "Combine images into a panorama" msgstr "Combina immagini lato a lato per creare un panorama" #: f.widgets.cc:320 msgid "Combine images into a vertical panorama" msgstr "Combina immagini in un panorama verticale" #: f.widgets.cc:321 msgid "Combine images into a panorama (panorama tools)" msgstr "Combina immagini lato a lato per creare un panorama (panorama tools)" #: f.widgets.cc:322 msgid "Arrange images and text in a layout (montage)" msgstr "Dispone immagini e testi in un collage" #: f.widgets.cc:325 msgid "Index new files and make thumbnails" msgstr "Aggiorna indici e miniature di nuovi file" #: f.widgets.cc:326 msgid "Change user preferences" msgstr "Modifica le preferenze" #: f.widgets.cc:327 msgid "Change Keyboard Shortcut Keys" msgstr "Modifica le scorciatoie da tastiera" #: f.widgets.cc:328 msgid "Show a brightness distribution graph" msgstr "Mostra il grafico di distribuzione della luminosità" #: f.widgets.cc:329 msgid "Show or revise grid lines" msgstr "Mostra o cambia la griglia" #: f.widgets.cc:330 msgid "Change color of foreground lines" msgstr "Cambia colore delle linee in primo piano" #: f.widgets.cc:331 msgid "Show RGB colors at mouse click" msgstr "Mostra i componenti colore RGB al clic" #: f.widgets.cc:332 msgid "Magnify image around the mouse position" msgstr "Ingrandisce temporaneamente una porzione d'immagine" #: f.widgets.cc:333 msgid "Highlight darkest and brightest pixels" msgstr "Individua i pixel di luminosità estrema" #: f.widgets.cc:334 msgid "Chart to adjust monitor color" msgstr "Grafico per regolare il colore dello schermo" #: f.widgets.cc:335 msgid "Chart to adjust monitor gamma" msgstr "Grafico per regolare la gamma dello schermo" #: f.widgets.cc:336 msgid "Change the GUI language" msgstr "Cambia la lingua dell'interfaccia" #: f.widgets.cc:337 msgid "Report missing translations" msgstr "Segnala traduzioni mancanti" #: f.widgets.cc:338 msgid "Calibrate printer colors" msgstr "" "Esegue una procedura per regolare la stampante e migliorare la resa dei " "colori" #: f.widgets.cc:339 msgid "Memory and CPU (to terminal/logfile)" msgstr "Memoria e CPU (al terminale/giornale)" #: f.widgets.cc:343 msgid "Rename/convert/resize/move multiple files" msgstr "Rinomina/converte/ridimensiona/sposta file multipli" #: f.widgets.cc:344 msgid "Upright multiple rotated image files" msgstr "Raddrizza immagini in massa" #: f.widgets.cc:345 msgid "Delete or Trash multiple files" msgstr "Elimina o cestina più immagini" #: f.widgets.cc:346 msgid "Convert camera RAW files using DCraw" msgstr "Converte file RAW usando DCraw" #: f.widgets.cc:347 msgid "Convert camera RAW files using Raw Therapee" msgstr "Converte file RAW usando Raw Therapee" #: f.widgets.cc:348 msgid "Build and run edit script files" msgstr "Compone ed esegue file script" #: f.widgets.cc:349 msgid "Burn selected image files to CD or DVD" msgstr "Incide le immagini selezionate su CD/DVD" #: f.widgets.cc:350 msgid "Search all image files and report duplicates" msgstr "Analizza tutte le immagini e segnala i duplicati" #: f.widgets.cc:354 msgid "Quick Start mini-guide" msgstr "Miniguida rapida" #: f.widgets.cc:355 msgid "Read the user guide" msgstr "Visualizza la guida utente" #: f.widgets.cc:356 msgid "Recent user guide changes" msgstr "Visualizza gli ultimi cambiamenti alla guida utente" #: f.widgets.cc:357 msgid "Summary of image edit functions" msgstr "Visualizza un sommario delle funzioni disponibili" #: f.widgets.cc:358 msgid "Technical installation notes" msgstr "Mostra note tecniche aggiuntive su Fotoxx" #: f.widgets.cc:359 msgid "List updates by Fotoxx version" msgstr "Mostra la storia delle versioni e loro contenuti" #: f.widgets.cc:360 msgid "View the log file and error messages" msgstr "Mostra il file di log: i messaggi generati durante l'avviamento" #: f.widgets.cc:361 msgid "How to do Fotoxx translations" msgstr "Mostra come fare la traduzione in altre lingue" #: f.widgets.cc:362 msgid "Show the Fotoxx web page" msgstr "Mostra la home page internet di fotoxx" #: f.widgets.cc:363 msgid "Version, license, contact, credits" msgstr "Versione, licenza, contatti, riconoscimenti" #: f.widgets.cc:366 msgid "Organize images into albums" msgstr "Organizza immagini in album" #: f.widgets.cc:367 msgid "Start a slide show" msgstr "Inizia uno slideshow " #: f.widgets.cc:368 msgid "Set desktop wallpaper from current Fotoxx image" msgstr "" #: f.widgets.cc:369 msgid "Cycle desktop wallpaper from a Fotoxx album" msgstr "" #: f.widgets.cc:391 msgid "New Window" msgstr "Nuova finestra" #: f.widgets.cc:392 msgid "Sync Gallery" msgstr "" #: f.widgets.cc:393 msgid "Recently Seen Images" msgstr "Immagini viste di recente" #: f.widgets.cc:394 msgid "Newest Images" msgstr "Immagini più nuove" #: f.widgets.cc:396 msgid "Open Previous File" msgstr "Apri l'immagine precedente" #: f.widgets.cc:397 msgid "Open RAW (ufraw)" msgstr "Apre file RAW (con ufraw)" #: f.widgets.cc:398 msgid "Open RAW (Raw Therapee)" msgstr "Apre file RAW (con Raw Therapee)" #: f.widgets.cc:399 msgid "New Blank Image" msgstr "Nuova immagine" #: f.widgets.cc:402 msgid "Delete Image File" msgstr "Elimina file" #: f.widgets.cc:403 msgid "Print Image" msgstr "Stampa immagine" #: f.widgets.cc:404 msgid "Print Calibrated Image" msgstr "Stampa immagine calibrata" #: f.widgets.cc:405 f.widgets.cc:550 msgid "Set Desktop Wallpaper" msgstr "" #: f.widgets.cc:409 msgid "View Metadata (short)" msgstr "Vedi metadati (breve)" #: f.widgets.cc:410 msgid "View Metadata (long)" msgstr "Vedi metadati (completo)" #: f.widgets.cc:411 msgid "Show Captions on Image" msgstr "Mostra/nasconde titoli e commenti in sovrimpressione" #: f.widgets.cc:413 msgid "Edit Any Metadata" msgstr "Modifica metadati (avanzato)" #: f.widgets.cc:416 f.widgets.cc:541 msgid "Batch Rename Tags" msgstr "Rinomina etichette in massa" #: f.widgets.cc:417 f.widgets.cc:542 msgid "Batch Add/Change Metadata" msgstr "Aggiunge/Modifica metadati in massa" #: f.widgets.cc:420 f.widgets.cc:544 f.widgets.cc:618 msgid "Images by Geotag" msgstr "Immagini tramite Geotag" #: f.widgets.cc:421 f.widgets.cc:545 f.widgets.cc:619 msgid "Search Images" msgstr "Ricerca immagini" #: f.widgets.cc:424 fotoxx.h:1168 msgid "Select" msgstr "Seleziona" #: f.widgets.cc:425 fotoxx.h:1170 msgid "Show" msgstr "Visualizza" #: f.widgets.cc:426 fotoxx.h:1124 msgid "Hide" msgstr "Nascondi" #: f.widgets.cc:427 fotoxx.h:1110 msgid "Enable" msgstr "Abilita" #: f.widgets.cc:428 fotoxx.h:1106 msgid "Disable" msgstr "Disabilita" #: f.widgets.cc:429 fotoxx.h:1127 msgid "Invert" msgstr "Inverti" #: f.widgets.cc:430 fotoxx.h:1184 msgid "Unselect" msgstr "Deseleziona" #: f.widgets.cc:431 msgid "Copy Area" msgstr "Copia area" #: f.widgets.cc:432 msgid "Paste Area" msgstr "Incolla area" #: f.widgets.cc:433 msgid "Open Area File" msgstr "Apri file di area" #: f.widgets.cc:434 msgid "Save Area File" msgstr "Salva file di area" #: f.widgets.cc:439 msgid "Voodoo 1" msgstr "Voodoo 1" #: f.widgets.cc:440 msgid "Voodoo 2" msgstr "Voodoo 2" #: f.widgets.cc:442 f.widgets.cc:687 msgid "Brightness Dist." msgstr "Dist. della luminosità" #: f.widgets.cc:443 f.widgets.cc:688 msgid "Zonal Flatten" msgstr "Appiattimento a zone" #: f.widgets.cc:447 msgid "Add Text" msgstr "Aggiungi scritte" #: f.widgets.cc:448 msgid "Add Lines" msgstr "Aggiunge linee" #: f.widgets.cc:451 msgid "Plugins" msgstr "Plugin" #: f.widgets.cc:456 msgid "Denoise" msgstr "Riduci disturbo" #: f.widgets.cc:458 msgid "Red Eyes" msgstr "Rimuovi occhi rossi" #: f.widgets.cc:463 msgid "Adjust RGB/CMY" msgstr "Regola RGB/CMY" #: f.widgets.cc:464 msgid "Adjust HSL" msgstr "Regola HSL" #: f.widgets.cc:465 msgid "Brightness Ramp" msgstr "Rampe di luce/colore" #: f.widgets.cc:467 msgid "Match Colors" msgstr "Allinea colori" #: f.widgets.cc:468 msgid "Color Profile" msgstr "Profilo di colore" #: f.widgets.cc:470 msgid "Anti-Alias" msgstr "Anti-Alias" #: f.widgets.cc:476 msgid "Fix Perspective" msgstr "Correggi prospettiva" #: f.widgets.cc:481 msgid "Flatten Book Page" msgstr "Appiattisci pagina" #: f.widgets.cc:484 msgid "Color Depth" msgstr "Risoluzione del colore" #: f.widgets.cc:485 msgid "Sketch" msgstr "Schizzo" #: f.widgets.cc:489 msgid "Embossing" msgstr "Bassorilievo" #: f.widgets.cc:491 msgid "Dots" msgstr "Puntinismo" #: f.widgets.cc:492 msgid "Painting" msgstr "Dipinto" #: f.widgets.cc:494 msgid "Texture" msgstr "Aggiungi disturbo" #: f.widgets.cc:496 msgid "Mosaic" msgstr "Mosaico" #: f.widgets.cc:503 msgid "High Dynamic Range" msgstr "Combina immagini (alta dinamica - HDR)" #: f.widgets.cc:504 msgid "High Depth of Field" msgstr "Combina immagini (grande profondità - HDF)" #: f.widgets.cc:505 msgid "Stack/Paint" msgstr "Sovrapponi (interattivo con mouse)" #: f.widgets.cc:506 msgid "Stack/Noise" msgstr "Sovrapponi (rimozione disturbo)" #: f.widgets.cc:507 msgid "Panorama" msgstr "Crea un panorama" #: f.widgets.cc:508 msgid "Vertical Panorama" msgstr "Panorama verticale" #: f.widgets.cc:509 msgid "PT Panorama" msgstr "Panorama (usa pano tools)" #: f.widgets.cc:510 msgid "Mashup" msgstr "Collage" #: f.widgets.cc:515 msgid "Keyboard Shortcuts" msgstr "Scorciatoie di tastiera" #: f.widgets.cc:516 msgid "Show Brightness Dist." msgstr "Mostra distribuzione luminosità" #: f.widgets.cc:521 msgid "Dark/Bright Pixels" msgstr "Punti chiari/scuri" #: f.widgets.cc:522 msgid "Monitor Color" msgstr "Colore monitor" #: f.widgets.cc:524 msgid "Change Language" msgstr "Cambia lingua del programma" #: f.widgets.cc:525 msgid "Missing Translations" msgstr "Traduzioni mancanti" #: f.widgets.cc:527 msgid "Resources" msgstr "Mostra risorse (nel terminale)" #: f.widgets.cc:534 msgid "Batch RAW (DCraw)" msgstr "Conversione RAW (DCraw) in massa" #: f.widgets.cc:535 msgid "Batch RAW (Raw Therapee)" msgstr "Conversione RAW (Raw Therapee) in massa" #: f.widgets.cc:536 msgid "Script Files" msgstr "File script" #: f.widgets.cc:537 msgid "Burn Images to CD/DVD" msgstr "Masterizza immagini su CD/DVD" #: f.widgets.cc:551 msgid "Cycle Desktop Wallpaper" msgstr "" #: f.widgets.cc:568 f.widgets.cc:591 f.widgets.cc:613 msgid "Gallery" msgstr "Galleria" #: f.widgets.cc:569 f.widgets.cc:592 f.widgets.cc:614 msgid "World Maps" msgstr "Mappe del mondo" #: f.widgets.cc:571 msgid "Favorites" msgstr "Preferiti" #: f.widgets.cc:573 fotoxx.h:1164 msgid "Save" msgstr "Salva" #: f.widgets.cc:574 msgid "Prev/Next" msgstr "Prec./Pross." #: f.widgets.cc:575 msgid "Metadata" msgstr "Metadati" #: f.widgets.cc:576 msgid "Areas" msgstr "Aree" #: f.widgets.cc:577 fotoxx.h:1109 msgid "Edit" msgstr "Modifica" #: f.widgets.cc:578 msgid "Repair" msgstr "Correggi" #: f.widgets.cc:579 msgid "Bend" msgstr "Deforma" #: f.widgets.cc:580 msgid "Effects" msgstr "Effetti" #: f.widgets.cc:581 msgid "Combine" msgstr "Combina" #: f.widgets.cc:582 msgid "Undo/Redo" msgstr "Annulla/ripeti" #: f.widgets.cc:583 msgid "Tools" msgstr "Strumenti" #: f.widgets.cc:594 msgid "Sync.G" msgstr "SincG" #: f.widgets.cc:595 msgid "Albums" msgstr "Album" #: f.widgets.cc:596 msgid "Bookmarks" msgstr "Segnalibri" #: f.widgets.cc:605 msgid "Batch" msgstr "In massa" #: f.widgets.cc:616 msgid "Choose Map" msgstr "Scegliere mappa" #: f.widgets.cc:617 msgid "Search Range" msgstr "Distanza di ricerca" #: f.widgets.cc:625 fotoxx.h:1182 msgid "Undo" msgstr "Annulla" #: f.widgets.cc:626 fotoxx.h:1155 msgid "Redo" msgstr "Ripeti" #: f.widgets.cc:667 msgid "Popup Image" msgstr "Comparsa immagine" #: f.widgets.cc:668 msgid "Popup Image (add)" msgstr "Mostra grande" #: f.widgets.cc:672 fotoxx.h:1158 msgid "Rename" msgstr "Rinomina" #: f.widgets.cc:673 msgid "Copy to Location" msgstr "Copia in una posizione" #: f.widgets.cc:674 msgid "Move to Location" msgstr "Sposta a una posizione" #: f.widgets.cc:675 msgid "Copy to Clipboard" msgstr "Copia negli appunti" #: f.widgets.cc:676 msgid "Remove from Album" msgstr "Rimuovere dall'album" #: f.widgets.cc:677 msgid "Cut to Image Cache" msgstr "Taglia verso verso il deposito immagini" #: f.widgets.cc:678 msgid "Copy to Image Cache" msgstr "Copia nel deposito immagini" #: f.widgets.cc:679 msgid "Paste Image Cache Here (clear)" msgstr "Incolla il deposito immagini qui (svuota)" #: f.widgets.cc:680 msgid "Paste Image Cache Here (keep)" msgstr "Incolla il deposito immagini qui (mantiene)" #: f.widgets.cc:681 msgid "Paste Current Image File Here" msgstr "Incolla qui l'immagine corrente" #: f.widgets.cc:685 msgid "Voodoo Enhance" msgstr "Miglioramento magico Voodoo" #: f.widgets.cc:690 msgid "Select Area" msgstr "Seleziona area" #: f.widgets.cc:693 fotoxx.h:1178 msgid "Trash" msgstr "Cestina" #: f.widgets.cc:819 msgid "Image Locations" msgstr "Locazioni delle immagini" #: fotoxx-15.11.cc:373 msgid "Please install missing programs:" msgstr "Installa i programmi mancanti:" #: fotoxx-15.11.cc:662 msgid "Kill active dialog?" msgstr "Terminare il dialogo corrente?" #: fotoxx-15.11.cc:733 msgid "(reduced)" msgstr "(ridotto)" #: fotoxx-15.11.cc:734 msgid "area active" msgstr "area attiva" #: fotoxx-15.11.cc:735 msgid "dialog open" msgstr "dialogo aperto" #: fotoxx-15.11.cc:736 msgid "blocked" msgstr "bloccato" #: fotoxx-15.11.cc:786 msgid "edits" msgstr "modifiche" #: fotoxx-15.11.cc:2793 msgid "Exceed 50 anchor points" msgstr "Superati 50 punti di ancoraggio" #: fotoxx-15.11.cc:3013 msgid "load curve from a file" msgstr "carica curva da file" #: fotoxx-15.11.cc:3079 msgid "curve file is invalid" msgstr "File della curva invalido" #: fotoxx-15.11.cc:3093 msgid "save curve to a file" msgstr "salva curva in un file" #: fotoxx-15.11.cc:3165 msgid "Too many edits, please save image" msgstr "Troppe modifiche: salva l'immagine" #: fotoxx-15.11.cc:3170 msgid "this function cannot be scripted" msgstr "Questa funzione non può essere usata in uno script" #: fotoxx-15.11.cc:3187 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Selezione di area non attiva.\n" "Continuare?" #: fotoxx-15.11.cc:3499 msgid "file data does not fit dialog" msgstr "I dati del file non si adattano alla finestra" #: fotoxx-15.11.cc:3509 msgid "Save settings to a file" msgstr "Salva impostazioni in un file" #: fotoxx-15.11.cc:3825 msgid "This action will discard changes to current image" msgstr "Questa azione annullerà le modifiche all'immagine" #: fotoxx-15.11.cc:3826 msgid "prior function still active" msgstr "Funzione precedente ancora attiva" #: fotoxx-15.11.cc:3827 fotoxx.h:1128 msgid "Keep" msgstr "Mantieni modifiche" #: fotoxx-15.11.cc:3828 msgid "Discard" msgstr "Perdi modifiche" #: fotoxx.h:1080 msgid "Add" msgstr "Aggiungi" #: fotoxx.h:1081 msgid "Add All" msgstr "Aggiungi tutto" #: fotoxx.h:1083 msgid "Amount" msgstr "Quantità" #: fotoxx.h:1084 msgid "Angle" msgstr "Angolo:" #: fotoxx.h:1085 msgid "Apply" msgstr "Applica" #: fotoxx.h:1086 msgid "Auto" msgstr "Auto" #: fotoxx.h:1087 msgid "Black" msgstr "Nero" #: fotoxx.h:1088 msgid "Blend Width" msgstr "Larghezza di sfumatura" #: fotoxx.h:1090 zfuncs.cc:10098 msgid "bottom" msgstr "Basso" #: fotoxx.h:1092 msgid "Browse" msgstr "Sfoglia" #: fotoxx.h:1093 msgid "Cancel" msgstr "Annulla" #: fotoxx.h:1094 msgid "Center" msgstr "centro" #: fotoxx.h:1095 msgid "Choose" msgstr "Scegli" #: fotoxx.h:1096 msgid "Clear" msgstr "Pulisci" #: fotoxx.h:1097 msgid "Color" msgstr "Colore" #: fotoxx.h:1098 msgid "continue" msgstr "continua" #: fotoxx.h:1100 msgid "Copy" msgstr "Copia" #: fotoxx.h:1101 msgid "Create" msgstr "Creare" #: fotoxx.h:1102 msgid "Curve File:" msgstr "File di curve:" #: fotoxx.h:1103 msgid "Cut" msgstr "Taglia" #: fotoxx.h:1104 msgid "Deband" msgstr "Elimina bande" #: fotoxx.h:1105 msgid "Delete" msgstr "Elimina" #: fotoxx.h:1107 msgid "Done" msgstr "Fatto" #: fotoxx.h:1108 msgid "edge" msgstr "bordo:" #: fotoxx.h:1111 msgid "Erase" msgstr "Esegui cancellatura" #: fotoxx.h:1112 msgid "Fetch" msgstr "Prendi" #: fotoxx.h:1113 msgid "output file already exists" msgstr "Il file di uscita esiste già" #: fotoxx.h:1114 #, c-format msgid "%d files selected" msgstr "%d file selezionati" #: fotoxx.h:1115 msgid "Find" msgstr "Cerca" #: fotoxx.h:1116 msgid "Finish" msgstr "Termina" #: fotoxx.h:1117 msgid "Flatten" msgstr "Quantità" #: fotoxx.h:1118 msgid "Font" msgstr "Font" #: fotoxx.h:1119 msgid "Geotags" msgstr "Geotags" #: fotoxx.h:1121 msgid "Grid" msgstr "Griglia" #: fotoxx.h:1122 msgid "Height" msgstr "Altezza" #: fotoxx.h:1125 msgid "Images" msgstr "Immagini" #: fotoxx.h:1126 msgid "Insert" msgstr "Incolla" #: fotoxx.h:1129 zfuncs.cc:10102 msgid "left" msgstr "Sinistra" #: fotoxx.h:1130 msgid "limit" msgstr "limite" #: fotoxx.h:1131 msgid "Load" msgstr "Carica" #: fotoxx.h:1132 msgid "Make" msgstr "Produci" #: fotoxx.h:1134 msgid "Map" msgstr "Mappa" #: fotoxx.h:1135 msgid "Max" msgstr "Massimo" #: fotoxx.h:1136 msgid "Negative" msgstr "Negativo" #: fotoxx.h:1137 msgid "New" msgstr "Nuovo" #: fotoxx.h:1138 msgid "Next" msgstr "Prossima" #: fotoxx.h:1139 zfuncs.cc:9190 msgid "No" msgstr "No" #: fotoxx.h:1140 msgid "no images" msgstr "nessuna immagine" #: fotoxx.h:1142 msgid "no selection" msgstr "nessuna selezione" #: fotoxx.h:1143 msgid "OK" msgstr "Ok" #: fotoxx.h:1145 msgid "Paste" msgstr "Incolla" #: fotoxx.h:1146 msgid "Pause" msgstr "Sospendi" #: fotoxx.h:1147 msgid "Percent" msgstr "Percentuale" #: fotoxx.h:1149 msgid "Presets" msgstr "Fisse: " #: fotoxx.h:1150 msgid "Prev" msgstr "Preced." #: fotoxx.h:1151 msgid "Proceed" msgstr "Prosegui" #: fotoxx.h:1153 msgid "range" msgstr "intervallo" #: fotoxx.h:1156 msgid "Reduce" msgstr "Riduci" #: fotoxx.h:1157 msgid "Remove" msgstr "Elimina" #: fotoxx.h:1159 msgid "Replace" msgstr "Rimpiazza" #: fotoxx.h:1160 msgid "Reserved" msgstr "Riservato" #: fotoxx.h:1161 msgid "Reset" msgstr "Azzera" #: fotoxx.h:1162 zfuncs.cc:10106 msgid "right" msgstr "Destra" #: fotoxx.h:1163 msgid "Rotate" msgstr "Ruota" #: fotoxx.h:1165 msgid "Unknown file type, save as tiff/jpeg/png to edit" msgstr "Tipo di file sconosciuto. Salvarlo come TIFF/JPEG/PNG per modificarlo" #: fotoxx.h:1166 msgid "Search" msgstr "Ricerca" #: fotoxx.h:1167 msgid "Seconds" msgstr "Secondi" #: fotoxx.h:1171 msgid "Size" msgstr "Dimensione" #: fotoxx.h:1172 msgid "Start" msgstr "Inizio" #: fotoxx.h:1173 msgid "Strength" msgstr "Intensità" #: fotoxx.h:1174 msgid "Threshold" msgstr "Soglia" #: fotoxx.h:1175 #, c-format msgid "exceed %d files" msgstr "più di %d file" #: fotoxx.h:1176 zfuncs.cc:10094 msgid "top" msgstr "alto" #: fotoxx.h:1177 msgid "Transparency" msgstr "Trasparenza:" #: fotoxx.h:1179 msgid "Trim" msgstr "Ritaglia" #: fotoxx.h:1180 msgid "Undo All" msgstr "Annulla tutto" #: fotoxx.h:1181 msgid "Undo Last" msgstr "Annulla ultimo" #: fotoxx.h:1183 msgid "Unfinish" msgstr "Ricomincia" #: fotoxx.h:1185 msgid "View" msgstr "Vista" #: fotoxx.h:1186 msgid "Web" msgstr "Web" #: fotoxx.h:1187 msgid "White" msgstr "Bianco" #: fotoxx.h:1188 msgid "Width" msgstr "Larghezza" #: fotoxx.h:1189 msgid "x-offset" msgstr "Origine X:" #: fotoxx.h:1190 msgid "y-offset" msgstr "Origine Y:" #: fotoxx.h:1191 zfuncs.cc:9190 msgid "Yes" msgstr "Si" #: zfuncs.cc:1601 #, c-format msgid "" "create directory? \n" " %s" msgstr "" "creare directory? \n" " %s" #: zfuncs.cc:4378 msgid "user guide not found" msgstr "Guida utente non trovata" #: zfuncs.cc:5196 #, c-format msgid "cannot open file %s" msgstr "non riesco ad aprire il file %s" #: zfuncs.cc:5232 msgid "save screen to file" msgstr "Salva schermo in un file" #: zfuncs.cc:9266 zfuncs.cc:9761 zfuncs.cc:10081 msgid "cancel" msgstr "Annulla" #: zfuncs.cc:9722 msgid "choose file" msgstr "Scegli file" #: zfuncs.cc:9727 msgid "choose files" msgstr "Scegli i file" #: zfuncs.cc:9732 msgid "save" msgstr "Salva" #: zfuncs.cc:9738 msgid "choose folder" msgstr "Scegli cartella" #: zfuncs.cc:9743 msgid "choose folders" msgstr "Scegli cartelle" #: zfuncs.cc:9748 msgid "create folder" msgstr "Crea cartella" #: zfuncs.cc:9755 msgid "hidden" msgstr "Nascosto" #: zfuncs.cc:10081 msgid "done" msgstr "Fatto" #: zfuncs.cc:10081 zfuncs.cc:10090 msgid "margins" msgstr "Margini" #: zfuncs.cc:10111 msgid "image scale" msgstr "Riscala immagine" #: zfuncs.cc:10113 msgid "percent" msgstr "percento" #: zfuncs.cc:10120 msgid "image" msgstr "immagine" #: zfuncs.cc:10124 msgid "width" msgstr "larghezza" #: zfuncs.cc:10128 msgid "height" msgstr "altezza" #~ msgid "Lock aspect ratio" #~ msgstr "Blocca proporzioni" #~ msgid "Median Brightness" #~ msgstr "Luminosità mediana" #~ msgid "Flatten Outliers 2" #~ msgstr "Modera picchi 2 (alternativo)" #~ msgid "Flatten Outliers 1" #~ msgstr "Modera picchi 1" #~ msgid "Kuwahara method" #~ msgstr "Metodo Kuwahara" #~ msgid "brightness gradient" #~ msgstr "Gradiente di luminosità" #~ msgid "unsharp mask" #~ msgstr "Maschera di contrasto" #~ msgid "Adjust Brightness Dist." #~ msgstr "Regola distribuzione luminosità" #~ msgid "" #~ "new name/base/adder unreasonable\n" #~ " e.g. newname ### 100 10" #~ msgstr "" #~ "nuovi nome/comune/addizione errati\n" #~ " es. nuovonome ### 100 10" #~ msgid "Cycle Desktop Image" #~ msgstr "Cicla sfondo desktop" #~ msgid "Set Desktop Image" #~ msgstr "Imposta sfondo desktop" #~ msgid "Cycle desktop images from a Fotoxx album" #~ msgstr "Cicla sfondo desktop da un album di Fotoxx" #~ msgid "Set desktop image from current Fotoxx image" #~ msgstr "Imposta sfondo desktop con l'immagine corrente" #~ msgid "Albums: Manage Albums, Slide Show, Desktop Background" #~ msgstr "Album: Gestione, Slide show, Sfondo desktop" #~ msgid "jpeg quality must be 1-100" #~ msgstr "la qualità jpeg deve essere 1-100" #~ msgid "(%d images)" #~ msgstr "(%d immagini)" #~ msgid "(0 images)" #~ msgstr "(0 immagini)" fotoxx-15.11.1/locales/translate-nl.po0000664000175000017500000030273012616075370016253 0ustar micomico# Translation of fotoxx-nl.po to dutch (nl) # msgid "" msgstr "" "Project-Id-Version: fotoxx 14.08\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-10-28 22:08+0100\n" "PO-Revision-Date: 2014-08-13 17:52+0100\n" "Last-Translator: Arthur Kalverboer \n" "Language-Team: Dutch\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" "X-Poedit-Language: Dutch\n" "X-Poedit-Country: NETHERLANDS\n" #: f.albums.cc:78 msgid "" "Right-click album thumbnail to cut/copy \n" "to cache, insert from cache, or remove." msgstr "" #: f.albums.cc:80 msgid "Drag album thumbnail to new position." msgstr "" #: f.albums.cc:109 f.widgets.cc:548 msgid "Manage Albums" msgstr "" #: f.albums.cc:120 f.albums.cc:258 msgid "Create or replace an album" msgstr "" #: f.albums.cc:122 msgid "Album to view or edit" msgstr "" #: f.albums.cc:124 msgid "Select images, add to cache" msgstr "Selecteer afbeeldingen, aan cache toevoegen" #: f.albums.cc:126 msgid "Clear image cache" msgstr "Schonen afbeeldings cache" #: f.albums.cc:128 f.albums.cc:150 msgid "Delete an album" msgstr "" #: f.albums.cc:149 msgid "Choose Album" msgstr "" #: f.albums.cc:151 #, c-format msgid "Image cache has %d images" msgstr "" #: f.albums.cc:152 msgid "cache added to empty album" msgstr "" #: f.albums.cc:207 #, c-format msgid "delete %s ?" msgstr "verwijder %s ?" #: f.albums.cc:242 #, c-format msgid "fill from image cache (%d images)" msgstr "vullen vanuit afbeeldings cache (%d afbeeldingen)" #: f.albums.cc:260 f.albums.cc:297 msgid "Album Name" msgstr "" #: f.albums.cc:266 msgid "make an initially empty album" msgstr "" #: f.albums.cc:268 msgid "fill from current gallery" msgstr "vullen vanuit huidige galerie" #: f.albums.cc:311 msgid "enter an album name" msgstr "" #: f.albums.cc:338 msgid "gallery is empty" msgstr "galerie is leeg" #: f.albums.cc:365 msgid "new album created" msgstr "" #: f.albums.cc:1099 msgid "Press ESC to exit slide show" msgstr "Druk ESC om diashow te stoppen" #: f.albums.cc:1101 msgid "instant" msgstr "moment" #: f.albums.cc:1102 msgid "fade-in" msgstr "fade-in" #: f.albums.cc:1103 msgid "roll-right" msgstr "rol-rechts" #: f.albums.cc:1104 msgid "roll-down" msgstr "rol-neer" #: f.albums.cc:1105 msgid "venetian" msgstr "venetiaan" #: f.albums.cc:1106 msgid "grate" msgstr "tralie" #: f.albums.cc:1107 msgid "rectangle" msgstr "rechthoek" #: f.albums.cc:1108 msgid "implode" msgstr "" #: f.albums.cc:1109 msgid "explode" msgstr "" #: f.albums.cc:1110 msgid "radar" msgstr "radar" #: f.albums.cc:1111 msgid "Japanese-fan" msgstr "" #: f.albums.cc:1112 msgid "jaws" msgstr "kaken" #: f.albums.cc:1113 msgid "ellipse" msgstr "ellips" #: f.albums.cc:1114 msgid "raindrops" msgstr "regendruppels" #: f.albums.cc:1115 msgid "doubledoor" msgstr "nieuw transitie type" #: f.albums.cc:1116 msgid "rotate" msgstr "draaien" #: f.albums.cc:1117 msgid "fallover" msgstr "" #: f.albums.cc:1118 msgid "sphereoid" msgstr "" #: f.albums.cc:1139 f.widgets.cc:549 msgid "Slide Show" msgstr "Diashow starten" #: f.albums.cc:1150 msgid "Clip Limit" msgstr "Clip Limiet" #: f.albums.cc:1154 msgid "Music File" msgstr "Muziek Bestand" #: f.albums.cc:1159 msgid "Full Screen" msgstr "Full Screen" #: f.albums.cc:1160 msgid "Auto-replay" msgstr "" #: f.albums.cc:1166 msgid "Customize:" msgstr "Aanpassen:" #: f.albums.cc:1167 msgid "transitions" msgstr "transities" #: f.albums.cc:1168 msgid "image files" msgstr "afbeeldings bestanden" #: f.albums.cc:1173 f.albums.cc:1289 #, c-format msgid "%d images" msgstr "%d afbeeldingen" #: f.albums.cc:1195 f.albums.cc:1259 f.albums.cc:1349 f.albums.cc:1462 #: f.albums.cc:1655 f.albums.cc:1739 f.albums.cc:1756 f.albums.cc:1977 msgid "invalid album" msgstr "" #: f.albums.cc:1273 msgid "open album" msgstr "" #: f.albums.cc:1305 msgid "Select music file" msgstr "" #: f.albums.cc:1329 msgid "select random (if 5+ enabled)" msgstr "selecteer random (als 5+ geactiveerd)" #: f.albums.cc:1353 msgid "Transition Preferences" msgstr "Transitie Voorkeuren" #: f.albums.cc:1362 msgid "transition" msgstr "transitie" #: f.albums.cc:1363 msgid "enabled" msgstr "geactiveerd" #: f.albums.cc:1364 msgid "slowdown" msgstr "vertraging" #: f.albums.cc:1365 msgid "preference" msgstr "voorkeur" #: f.albums.cc:1468 msgid "Image Preferences" msgstr "Afbeeldings Voorkeuren" #: f.albums.cc:1472 f.file.cc:1159 f.file.cc:1400 f.file.cc:1608 f.meta.cc:437 msgid "Image File:" msgstr "Afbeeldingsbestand:" #: f.albums.cc:1476 msgid "Play tone when image shows" msgstr "" #: f.albums.cc:1479 msgid "Show image caption" msgstr "" #: f.albums.cc:1484 msgid "Show image comments" msgstr "" #: f.albums.cc:1489 msgid "Wait before zoom" msgstr "" #: f.albums.cc:1494 msgid "Zoom type:" msgstr "" #: f.albums.cc:1495 msgid "none" msgstr "" #: f.albums.cc:1496 msgid "zoom-in" msgstr "" #: f.albums.cc:1497 msgid "zoom-out" msgstr "" #: f.albums.cc:1500 msgid "Zoom size (x)" msgstr "" #: f.albums.cc:1503 msgid "Steps" msgstr "Stappen" #: f.albums.cc:1507 msgid "Zoom image location:" msgstr "" #: f.albums.cc:1515 msgid "Wait after zoom" msgstr "" #: f.albums.cc:1520 msgid "Transition to next image" msgstr "" #: f.albums.cc:1523 f.albums.cc:1625 msgid "next" msgstr "volgende" #: f.albums.cc:1962 f.albums.cc:2120 #, c-format msgid "" "file format error: \n" " %s" msgstr "" #: f.area.cc:79 msgid "Select Area for Edits" msgstr "Uitsnede om te bewerken" #: f.area.cc:80 f.edit.cc:5927 msgid "Press F1 for help" msgstr "Druk F1 voor help" #: f.area.cc:89 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Uitsnede niet ondersteund \n" "door deze bewerkingsfunctie" #: f.area.cc:119 msgid "select rectangle" msgstr "selecteer rechthoek" #: f.area.cc:120 msgid "select ellipse" msgstr "selecteer ellips" #: f.area.cc:123 msgid "freehand draw" msgstr "vrijehand tekenen" #: f.area.cc:124 msgid "follow edge" msgstr "rand volgen" #: f.area.cc:125 msgid "draw/replace" msgstr "tekenen/vervangen" #: f.area.cc:128 msgid "select area within mouse" msgstr "uitsnede maken binnen muis" #: f.area.cc:131 msgid "select one matching color within mouse" msgstr "selecteer een matchende kleur in muis" #: f.area.cc:135 msgid "select all matching colors within mouse" msgstr "selecteer alle matchende kleuren binnen muis" #: f.area.cc:139 f.edit.cc:5972 msgid "mouse radius" msgstr "muis straal" #: f.area.cc:142 msgid "match level %" msgstr "match nivo %" #: f.area.cc:147 msgid "search range" msgstr "zoek bereik" #: f.area.cc:149 msgid "firewall" msgstr "firewall" #: f.area.cc:152 msgid "Area Edge Blend Width" msgstr "Uitsnede Rand Meng Breedte" #: f.area.cc:155 msgid "Edge Creep" msgstr "Rand Vernauwen/Verwijden" #: f.area.cc:160 msgid "Line Color:" msgstr "" #: f.area.cc:180 f.area.cc:181 msgid "area edits fade away within edge distance" msgstr "uitsnede bewerkingen vervagen binnen rand afstand" #: f.area.cc:360 f.area.cc:543 #, c-format msgid "exceed %d edits" msgstr "overschrijding %d bewerkingen" #: f.area.cc:1364 msgid "" "Click one time inside each enclosed area \n" "(possible gaps in the outline will be found). \n" "Press F1 for help." msgstr "" "Klik eenmalig in elke uitsnede \n" "(mogelijke gaten in de contouren worden gezocht). \n" "Druk F1 voor help." #: f.area.cc:1392 msgid "finish area" msgstr "gereedmaken uitsnede" #: f.area.cc:1400 msgid "extend to corner:" msgstr "" #: f.area.cc:1441 msgid "searching" msgstr "bezig met zoeken" #: f.area.cc:1472 msgid "outline has a gap" msgstr "contour heeft een gat" #: f.area.cc:1476 msgid "success" msgstr "succes" #: f.area.cc:1613 f.area.cc:1640 #, c-format msgid "found %d pixels" msgstr "gevonden %d pixels" #: f.area.cc:1935 f.area.cc:1952 f.area.cc:1972 f.area.cc:2287 f.area.cc:2363 #: f.area.cc:2520 msgid "the area is not finished" msgstr "de uitsnede is niet gereed" #: f.area.cc:2061 msgid "Edge calculation in progress" msgstr "Berekening rand bezig" #: f.area.cc:2070 msgid "Area Edge Calc" msgstr "Randberekening van uitsnede" #: f.area.cc:2471 msgid "load area from a file" msgstr "" #: f.area.cc:2554 msgid "save area as a PNG file" msgstr "" #: f.area.cc:2582 msgid "position with mouse click/drag" msgstr "positioneer met muis klik/sleep" #: f.area.cc:2638 msgid "Paste Image" msgstr "Afbeelding plakken" #: f.area.cc:2643 f.batch.cc:1169 f.batch.cc:1660 msgid "resize" msgstr "herschalen" #: f.batch.cc:107 f.widgets.cc:531 msgid "Batch Convert" msgstr "Batchgewijs Converteren" #: f.batch.cc:114 f.file.cc:942 msgid "New Name" msgstr "nieuwe naam" #: f.batch.cc:118 msgid "Sequence Numbers" msgstr "" #: f.batch.cc:120 msgid "base" msgstr "basis" #: f.batch.cc:122 msgid "adder" msgstr "toevoeging" #: f.batch.cc:127 msgid "New Location" msgstr "Nieuwe Locatie" #: f.batch.cc:132 msgid "New File Type" msgstr "Nieuw Bestands Type" #: f.batch.cc:136 f.batch.cc:144 f.repair.cc:3861 msgid "no change" msgstr "geen verandering" #: f.batch.cc:139 msgid "max. Width" msgstr "max. Breedte" #: f.batch.cc:147 f.batch.cc:565 msgid "Delete Originals" msgstr "Verwijder Originelen" #: f.batch.cc:148 f.batch.cc:562 msgid "Copy Metadata" msgstr "Kopieren Metadata" #: f.batch.cc:149 f.batch.cc:563 f.edit.cc:1413 f.widgets.cc:438 #: f.widgets.cc:684 msgid "Upright" msgstr "Rechtop" #: f.batch.cc:152 f.batch.cc:564 f.batch.cc:1178 f.batch.cc:1669 #: f.repair.cc:118 f.widgets.cc:454 msgid "Sharpen" msgstr "Afbeelding verscherpen" #: f.batch.cc:154 f.batch.cc:1180 f.batch.cc:1671 msgid "amount" msgstr "hoeveelheid" #: f.batch.cc:157 f.batch.cc:1183 f.batch.cc:1674 msgid "threshold" msgstr "drempel" #: f.batch.cc:280 msgid "file type not supported" msgstr "bestandstype niet ondersteund" #: f.batch.cc:344 msgid "cannot create new file" msgstr "kan nieuw bestand niet aanmaken" #: f.batch.cc:391 msgid "updating albums ..." msgstr "" #: f.batch.cc:464 f.batch.cc:1456 f.batch.cc:1862 f.file.cc:1208 msgid "Select directory" msgstr "Selecteer map" #: f.batch.cc:513 msgid "" "new name/base/adder unreasonable\n" " e.g. newname ### 100 10\n" " or ... [oldname] ..." msgstr "" #: f.batch.cc:537 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "max. grootte %d x %d is niet logisch" #: f.batch.cc:556 #, c-format msgid "Convert %d image files" msgstr "Converteren %d afbeeldingen" #: f.batch.cc:557 msgid "Rename to" msgstr "Hernoemen naar" #: f.batch.cc:558 msgid "Convert to" msgstr "Converteren naar" #: f.batch.cc:559 msgid "Resize within" msgstr "Herschalen in" #: f.batch.cc:560 msgid "Output to" msgstr "Uitvoer naar" #: f.batch.cc:566 msgid "PROCEED?" msgstr "DOORGAAN?" #: f.batch.cc:720 f.widgets.cc:532 msgid "Batch Upright" msgstr "Batchgewijs Rechtop" #: f.batch.cc:726 msgid "Survey all files" msgstr "" #: f.batch.cc:780 msgid "file cannot be read" msgstr "" #: f.batch.cc:898 msgid "cannot select both options" msgstr "" #: f.batch.cc:942 f.widgets.cc:533 msgid "Batch Delete/Trash" msgstr "" #: f.batch.cc:947 msgid "delete" msgstr "" #: f.batch.cc:950 msgid "trash" msgstr "" #: f.batch.cc:1112 msgid "Batch Convert RAW Files (DCraw)" msgstr "Batchgewijs Converteren RAW Bestanden (DCraw)" #: f.batch.cc:1121 msgid "DCraw not installed" msgstr "DCraw niet geinstalleerd" #: f.batch.cc:1156 f.batch.cc:1647 msgid "output location" msgstr "uitvoer locatie" #: f.batch.cc:1161 f.batch.cc:1652 msgid "output file type" msgstr "output bestandstype" #: f.batch.cc:1192 msgid "white balance" msgstr "witbalans" #: f.batch.cc:1193 msgid "interpolation" msgstr "interpolatie" #: f.batch.cc:1194 msgid "color space" msgstr "kleurruimte" #: f.batch.cc:1195 msgid "gamma curve" msgstr "gamma curve" #: f.batch.cc:1198 msgid "camera" msgstr "camera" #: f.batch.cc:1199 msgid "fixed" msgstr "vast" #: f.batch.cc:1200 msgid "calculated" msgstr "berekend" #: f.batch.cc:1217 msgid "default" msgstr "default" #: f.batch.cc:1223 msgid "defaults" msgstr "defaults" #: f.batch.cc:1610 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Batch Converteren RAW Bestanden (Raw Therapee)" #: f.batch.cc:1619 f.file.cc:428 msgid "Raw Therapee not installed" msgstr "Raw Therapee not installed" #: f.batch.cc:1959 msgid "start" msgstr "" #: f.batch.cc:1960 msgid "begin making a script file" msgstr "" #: f.batch.cc:1962 msgid "close" msgstr "" #: f.batch.cc:1963 msgid "finish making a script file" msgstr "" #: f.batch.cc:1965 msgid "run" msgstr "" #: f.batch.cc:1966 msgid "execute a script file" msgstr "" #: f.batch.cc:2020 msgid "script already started" msgstr "" #: f.batch.cc:2024 msgid "open new script file" msgstr "" #: f.batch.cc:2056 msgid "script file error" msgstr "" #: f.batch.cc:2061 #, c-format msgid "%s added to script" msgstr "" #: f.batch.cc:2074 msgid "no script file was started" msgstr "" #: f.batch.cc:2082 msgid "script file closed" msgstr "" #: f.batch.cc:2101 msgid "script file is not closed" msgstr "" #: f.batch.cc:2105 msgid "select script file to run" msgstr "" #: f.batch.cc:2107 msgid "open script file" msgstr "" #: f.batch.cc:2114 msgid "unknown script file" msgstr "" #: f.batch.cc:2121 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" #: f.batch.cc:2132 msgid "select image files to be processed" msgstr "" #: f.batch.cc:2144 f.batch.cc:2193 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" #: f.batch.cc:2165 #, c-format msgid "unknown edit function: %s" msgstr "" #: f.batch.cc:2174 #, c-format msgid "load widgets failed: %s" msgstr "" #: f.batch.cc:2203 #, c-format msgid "script file format error: %s" msgstr "" #: f.batch.cc:2229 msgid "Brasero not installed" msgstr "Brasero niet geinstalleerd" #: f.batch.cc:2318 f.widgets.cc:538 msgid "Find Duplicate Images" msgstr "" #: f.batch.cc:2320 f.tools.cc:929 msgid "Thumbnail size" msgstr "" #: f.batch.cc:2323 msgid "pixel difference" msgstr "" #: f.batch.cc:2326 msgid "pixel count" msgstr "" #: f.batch.cc:2329 msgid "Images:" msgstr "" #: f.batch.cc:2330 msgid "searching ..." msgstr "" #: f.batch.cc:2332 msgid "Duplicates:" msgstr "" #: f.batch.cc:2350 #, c-format msgid "only %d image thumbnails found" msgstr "" #: f.bend.cc:86 f.widgets.cc:475 msgid "Unbend" msgstr "Ontkrommen" #: f.bend.cc:94 f.edit.cc:4267 f.effects.cc:4725 msgid "vertical" msgstr "verticaal" #: f.bend.cc:95 f.edit.cc:4266 f.effects.cc:4721 msgid "horizontal" msgstr "horizontaal" #: f.bend.cc:96 msgid "linear" msgstr "lineair" #: f.bend.cc:99 msgid "curved" msgstr "gebogen" #: f.bend.cc:367 msgid "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." msgstr "" "Klik op de hoeken van een vierhoek. Druk [Toepassen]. \n" "De afbeelding wordt vervormd zodat de vierhoek in een \n" "rechthoek veranderd. " #: f.bend.cc:384 msgid "Perspective Correction" msgstr "Perspectief Correctie" #: f.bend.cc:590 msgid "must have 4 corners" msgstr "4 hoeken verplicht" #: f.bend.cc:712 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 "" " Selecteer een uitsnede met menu-functie [Uitsnede selecteren]. \n" " Druk [start kromtrekken] en sleep uitsnede met de muis. \n" " Herhalen tot het gewenste resultaat. \n" " Indien gereed, een andere uitsnede selecteren of druk [Klaar]." #: f.bend.cc:725 f.widgets.cc:477 msgid "Warp area" msgstr "Kromtrekken uitsnede" #: f.bend.cc:730 msgid "start warp" msgstr "Start kromtrekken" #: f.bend.cc:788 msgid "no active Select Area" msgstr "geen actieve Uitsnede" #: f.bend.cc:1129 f.bend.cc:1443 msgid "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Sleep de plaats van een afbeelding met de muis. \n" " Herhalen tot het gewenste resultaat. \n" " Indien gereed, druk [Klaar]." #: f.bend.cc:1147 f.widgets.cc:478 msgid "Warp curved" msgstr "Kromtrekken gebogen" #: f.bend.cc:1156 f.mashup.cc:1836 msgid "warp span" msgstr "kromtrekken spanwijdte" #: f.bend.cc:1461 f.widgets.cc:479 msgid "Warp linear" msgstr "Kromtrekken lineair" #: f.bend.cc:1776 msgid "" " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Sleep een hoek van de afbeelding. \n" " Herhalen tot het gewenste resultaat. \n" " Indien gereed, druk [Klaar]." #: f.bend.cc:1792 f.widgets.cc:480 msgid "Warp affine" msgstr "Kromtrekken affine" #: f.bend.cc:2138 msgid "Flatten Book Page Photo" msgstr "Platstrijken Foto van Pagina" #: f.bend.cc:2139 msgid "" "Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: " msgstr "" "Bijsnijden om een pagina te isoleren. \n" "Markeer bovenste en onderste randen \n" "met 4+ muisklikken, dan platstrijken:" #: f.bend.cc:2142 msgid "Stretch curved-down surfaces:" msgstr "Strekken gekromde oppervlakken:" #: f.bend.cc:2197 msgid "top:" msgstr "boven:" #: f.bend.cc:2200 msgid "bottom:" msgstr "onder:" #: f.combine.cc:2108 f.combine.cc:2740 f.combine.cc:3396 f.combine.cc:3928 msgid "Select 2 to 9 files" msgstr "Selecteer 2 tot 9 bestanden" #: f.combine.cc:2129 f.combine.cc:2761 f.combine.cc:3417 f.combine.cc:3949 msgid "Images are not all the same size" msgstr "Afbeeldingen hebben niet dezelfde grootte" #: f.combine.cc:2473 msgid "Adjust Image Contributions" msgstr "Instellen helderheidsbijdragen per afbeelding" #: f.combine.cc:2477 msgid "dark pixels" msgstr "donkere pixels" #: f.combine.cc:2479 msgid "light pixels" msgstr "heldere pixels" #: f.combine.cc:2481 msgid "file:" msgstr "bestand:" #: f.combine.cc:2997 msgid "Paint and Warp Image" msgstr "Schilderen en kromtrekken afbeelding" #: f.combine.cc:3001 f.combine.cc:3646 f.combine.cc:6237 f.effects.cc:3912 #: f.widgets.cc:567 f.widgets.cc:590 f.widgets.cc:612 msgid "Image" msgstr "Afbeelding" #: f.combine.cc:3005 msgid "paint" msgstr "schilderen" #: f.combine.cc:3006 msgid "warp" msgstr "kromtrekken" #: f.combine.cc:3644 msgid "Select and Paint Image" msgstr "Selecteer en schilder afbeelding" #: f.combine.cc:4120 msgid "Adjust Pixel Composition" msgstr "Instellen Pixel Samenstelling" #: f.combine.cc:4122 msgid "use average" msgstr "gebruik gemiddelde" #: f.combine.cc:4123 msgid "use median" msgstr "gebruik mediaan" #: f.combine.cc:4125 msgid "omit low pixel" msgstr "weglaten lage pixels" #: f.combine.cc:4126 msgid "omit high pixel" msgstr "weglaten hoge pixels" #: f.combine.cc:4388 f.combine.cc:5494 msgid "Select 2 to 4 files" msgstr "Selecteer 2 tot 4 bestanden" #: f.combine.cc:4463 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from lower edge." msgstr "" "Sleep afbeeldingen voor ruwe uitlijning.\n" "Om te draaien, sleep de onderste rand." #: f.combine.cc:4465 f.combine.cc:5571 msgid "no curve (scanned image)" msgstr "geen curve (gescande afbeelding)" #: f.combine.cc:4466 f.combine.cc:5572 msgid "Search for lens mm" msgstr "Zoeken naar brandpuntsafstand" #: f.combine.cc:4467 f.combine.cc:5573 msgid "Save lens mm → image EXIF" msgstr "Bewaar lens mm → afbeelding EXIF" #: f.combine.cc:4528 f.combine.cc:5634 msgid "Pre-align Images" msgstr "Afbeelding globaal positioneren" #: f.combine.cc:4532 f.combine.cc:5638 msgid "lens mm" msgstr "brandpuntsafstand mm" #: f.combine.cc:4537 f.combine.cc:5643 msgid "no auto warp" msgstr "geen auto kromtrekken" #: f.combine.cc:4539 f.combine.cc:5645 f.widgets.cc:445 f.widgets.cc:683 msgid "Resize" msgstr "Herschalen" #: f.combine.cc:4540 f.combine.cc:5646 msgid "resize window" msgstr "herschalen venster" #: f.combine.cc:4548 f.combine.cc:5654 msgid "do not warp images during auto-alignment" msgstr "niet kromtrekken gedurende auto-uitlijning" #: f.combine.cc:4593 f.combine.cc:5699 msgid "use two images only" msgstr "gebruik slechts twee afbeeldingen" #: f.combine.cc:4621 f.combine.cc:4825 f.combine.cc:5011 f.combine.cc:5725 #: f.combine.cc:5929 f.combine.cc:6115 msgid "Too little overlap, cannot align" msgstr "Te weinig overlap, uitlijnen niet mogelijk" #: f.combine.cc:5089 f.combine.cc:6193 msgid "Match Brightness and Color" msgstr "Match helderheid en kleur" #: f.combine.cc:5134 msgid "Select image" msgstr "Selecteer afbeelding" #: f.combine.cc:5149 f.combine.cc:6252 msgid "auto color" msgstr "auto kleur" #: f.combine.cc:5150 f.combine.cc:6253 msgid "file color" msgstr "bestand kleur" #: f.combine.cc:5156 f.combine.cc:6259 msgid "mouse warp" msgstr "muis kromtrekken" #: f.combine.cc:5159 msgid "curved image" msgstr "" #: f.combine.cc:5569 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from right edge." msgstr "" "Sleep afbeeldingen voor ruwe uitlijning.\n" "Om te draaien, sleep de rechter rand." #: f.combine.cc:6469 msgid "pano tools (hugin) not installed" msgstr "" #: f.combine.cc:6478 msgid "Select at least 2 files" msgstr "" #: f.edit.cc:98 msgid "Trim: drag middle to move, drag corners to resize" msgstr "" "Bijsnijden: sleep midden om te verplaatsen, sleep hoeken om te herschalen" #: f.edit.cc:99 msgid "Minor rotate: drag right edge with mouse" msgstr "Kleine draaiing: sleep rechter rand met de muis" #: f.edit.cc:173 f.widgets.cc:437 f.widgets.cc:682 msgid "Trim/Rotate" msgstr "Bijsnijden/Draaien" #: f.edit.cc:185 f.edit.cc:586 msgid "ratio" msgstr "ratio" #: f.edit.cc:189 msgid "trim size:" msgstr "bijsnijd grootte:" #: f.edit.cc:194 msgid "Lock Ratio" msgstr "verhoudingen vasthouden" #: f.edit.cc:199 msgid "Customize" msgstr "Aanpassen" #: f.edit.cc:207 msgid "Rotate: degrees" msgstr "Draaien: graden" #: f.edit.cc:209 msgid "auto-trim" msgstr "auto-bijsnijden" #: f.edit.cc:582 msgid "Trim Buttons" msgstr "Bijsnijd knoppen" #: f.edit.cc:585 msgid "label" msgstr "label" #: f.edit.cc:1411 msgid "Upright Image" msgstr "" #: f.edit.cc:1477 msgid "rotation unknown" msgstr "" #: f.edit.cc:2029 f.widgets.cc:441 f.widgets.cc:686 msgid "Retouch Combo" msgstr "Retoucheer Combo" #: f.edit.cc:2050 msgid "Amplifier" msgstr "Versterker" #: f.edit.cc:2051 fotoxx.h:1091 msgid "Brightness" msgstr "helderheid" #: f.edit.cc:2052 f.effects.cc:3364 fotoxx.h:1099 msgid "Contrast" msgstr "Contrast" #: f.edit.cc:2053 msgid "Low Color" msgstr "Laag Kleur" #: f.edit.cc:2054 msgid "Warmer" msgstr "Warmer" #: f.edit.cc:2055 f.effects.cc:1011 msgid "Dark Areas" msgstr "Donkere gebieden" #: f.edit.cc:2064 msgid "Max." msgstr "Max." #: f.edit.cc:2065 f.edit.cc:2066 f.edit.cc:2067 msgid "High" msgstr "Hoog" #: f.edit.cc:2068 msgid "Cooler" msgstr "Koeler" #: f.edit.cc:2069 msgid "Bright" msgstr "Licht" #: f.edit.cc:2072 f.tools.cc:1631 msgid "Brightness Distribution" msgstr "Helderheids Verdeling" #: f.edit.cc:2075 msgid "Click for white balance or black level" msgstr "Klik voor witbalans of zwart nivo" #: f.edit.cc:2078 msgid "Settings File" msgstr "Instellingen Bestand" #: f.edit.cc:2082 msgid "recall previous settings used" msgstr "terughalen hiervoor gebruikte instellingen" #: f.edit.cc:2726 msgid "Adjust Brightness Distribution" msgstr "Instellen Helderheids Verdeling" #: f.edit.cc:2765 msgid "Low Cutoff" msgstr "" #: f.edit.cc:2766 msgid "High Cutoff" msgstr "" #: f.edit.cc:2767 msgid "Low Flatten" msgstr "" #: f.edit.cc:2768 msgid "Mid Flatten" msgstr "" #: f.edit.cc:2769 msgid "High Flatten" msgstr "" #: f.edit.cc:2770 msgid "Low Stretch" msgstr "" #: f.edit.cc:2771 msgid "Mid Stretch" msgstr "" #: f.edit.cc:2772 msgid "High Stretch" msgstr "" #: f.edit.cc:3102 msgid "Zonal Flatten Brightness" msgstr "" #: f.edit.cc:3135 msgid "Zones" msgstr "" #: f.edit.cc:3142 msgid "Deband Dark" msgstr "" #: f.edit.cc:3145 msgid "Deband Bright" msgstr "" #: f.edit.cc:3594 f.widgets.cc:444 f.widgets.cc:689 msgid "Tone Mapping" msgstr "Tone mapping instellen" #: f.edit.cc:3625 msgid "low" msgstr "laag" #: f.edit.cc:3627 msgid "high" msgstr "hoog" #: f.edit.cc:3630 msgid "Amplify" msgstr "Versterken" #: f.edit.cc:4034 msgid "Resize Image" msgstr "Herschalen Afbeelding" #: f.edit.cc:4059 msgid "W/H Ratio:" msgstr "" #: f.edit.cc:4061 msgid "Lock" msgstr "" #: f.edit.cc:4063 msgid "use previous settings" msgstr "" #: f.edit.cc:4262 f.widgets.cc:446 msgid "Flip" msgstr "Afbeelding spiegelen" #: f.edit.cc:4368 msgid "+Version" msgstr "" #: f.edit.cc:4391 msgid "Write Text on Image" msgstr "Schrijf Tekst op Afbeelding" #: f.edit.cc:4392 msgid "Enter text, click/drag on image, right click to remove" msgstr "" "Invoeren tekst, klik/sleep in afbeelding, rechter klik om te verwijderen" #: f.edit.cc:4441 f.mashup.cc:2437 msgid "Text" msgstr "Tekst" #: f.edit.cc:4446 msgid "Use metadata key" msgstr "" #: f.edit.cc:4451 msgid "Use text file" msgstr "" #: f.edit.cc:4470 f.mashup.cc:2455 msgid "text" msgstr "tekst" #: f.edit.cc:4471 f.edit.cc:5309 f.mashup.cc:2456 f.mashup.cc:2798 msgid "backing" msgstr "achtergrondkleur" #: f.edit.cc:4472 f.edit.cc:5310 f.mashup.cc:2457 f.mashup.cc:2799 msgid "outline" msgstr "contour" #: f.edit.cc:4473 f.edit.cc:5311 f.mashup.cc:2458 f.mashup.cc:2800 msgid "shadow" msgstr "schaduw" #: f.edit.cc:4499 msgid "save to current file" msgstr "" #: f.edit.cc:4500 f.file.cc:1909 msgid "save as new file version" msgstr "bewaren als nieuwe bestandsversie" #: f.edit.cc:4501 msgid "" "save to current file \n" "open next file with same text" msgstr "" #: f.edit.cc:4662 f.mashup.cc:2557 f.tools.cc:1068 msgid "select font" msgstr "selecteer font" #: f.edit.cc:4926 msgid "text file is defective" msgstr "tekstbestand is beschadigd" #: f.edit.cc:5249 msgid "" "Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove" msgstr "" "Invoeren lijn of pijl eigenschappen in dialoog, \n" "klik/sleep op afbeelding, rechterklik om te verwijderen" #: f.edit.cc:5280 msgid "Write Line or Arrow on Image" msgstr "Schrijf Lijn of Pijl op Afbeelding" #: f.edit.cc:5288 f.mashup.cc:2777 msgid "Line length" msgstr "Lijn lengte" #: f.edit.cc:5295 f.mashup.cc:2784 msgid "Arrow head" msgstr "Pijlpunt" #: f.edit.cc:5308 f.mashup.cc:2797 msgid "line" msgstr "lijn" #: f.edit.cc:5337 msgid "" "fix line/arrow in layout \n" " start new line/arrow" msgstr "" "fix lijn/pijl in layout \n" " begin nieuwe lijn/pijl" #: f.edit.cc:5926 f.widgets.cc:449 msgid "Paint Edits" msgstr "Veranderingen schilderen" #: f.edit.cc:5933 f.edit.cc:6162 fotoxx-15.11.cc:3179 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "Uitsnede kan niet bewaard worden.\n" "Doorgaan?" #: f.edit.cc:5941 f.edit.cc:6170 f.tools.cc:2157 msgid "Edit function must be active" msgstr "Bewerkingsfunctie moet actief zijn" #: f.edit.cc:5946 msgid "Cannot use Paint Edits" msgstr "" #: f.edit.cc:5975 msgid "power: center" msgstr "kracht: centrum" #: f.edit.cc:5980 msgid "reset area" msgstr "reset uitsnede" #: f.edit.cc:6155 f.widgets.cc:450 msgid "Leverage Edits" msgstr "Veranderingen versterken" #: f.edit.cc:6156 msgid "Edit Function Amplifier" msgstr "Versterker functie wijzigen" #: f.edit.cc:6175 msgid "Cannot use Leverage Edits" msgstr "" #: f.edit.cc:6208 msgid "minimum" msgstr "minimum" #: f.edit.cc:6210 msgid "maximum" msgstr "maximum" #: f.edit.cc:6510 msgid "Edit Plugins" msgstr "Plugins aanpassen" #: f.edit.cc:6511 msgid "Edit plugins menu" msgstr "" #: f.edit.cc:6519 msgid "Run as Fotoxx edit function" msgstr "Uitvoeren als Fotoxx bewerkingsfunctie" #: f.edit.cc:6757 msgid "Plugin working ..." msgstr "Plugin bezig ..." #: f.edit.cc:6766 msgid "plugin failed" msgstr "plugin werkt niet" #: f.effects.cc:64 msgid "Set color depth to 1-16 bits" msgstr "Instellen kleurdiepte op 1-16 bits" #: f.effects.cc:76 msgid "Set Color Depth" msgstr "Instellen Kleurdiepte" #: f.effects.cc:220 msgid "Convert to Sketch" msgstr "Converteren naar Schets" #: f.effects.cc:296 msgid "Clip Level" msgstr "Clip Nivo" #: f.effects.cc:300 msgid "Algorithm" msgstr "Algoritme" #: f.effects.cc:305 msgid "Foreground" msgstr "" #: f.effects.cc:308 msgid "Background" msgstr "" #: f.effects.cc:714 f.widgets.cc:486 msgid "Line Drawing" msgstr "Lijn Tekenen" #: f.effects.cc:727 msgid "black/white" msgstr "zwart/wit" #: f.effects.cc:1005 f.widgets.cc:487 msgid "Color Drawing" msgstr "Kleur Tekenen" #: f.effects.cc:1012 msgid "Bright Areas" msgstr "Heldere Uitsnedes" #: f.effects.cc:1266 f.widgets.cc:488 msgid "Graduated Blur" msgstr "Graduele Vervaging" #: f.effects.cc:1270 msgid "Contrast Limit" msgstr "Contrast limiet" #: f.effects.cc:1273 f.repair.cc:775 msgid "Blur Radius" msgstr "Vervagingsstraal" #: f.effects.cc:1487 msgid "Simulate Embossing" msgstr "Reliefdruk nabootsen" #: f.effects.cc:1504 msgid "depth" msgstr "diepte" #: f.effects.cc:1706 msgid "Simulate Tiles" msgstr "Tegels nabootsen" #: f.effects.cc:1710 msgid "tile size" msgstr "tegelgrootte" #: f.effects.cc:1713 msgid "tile gap" msgstr "voegbreedte" #: f.effects.cc:1716 msgid "3D depth" msgstr "3D diepte" #: f.effects.cc:1940 msgid "Convert Image to Dots" msgstr "Converteer naar krantendruk" #: f.effects.cc:1944 msgid "dot size" msgstr "stip grootte" #: f.effects.cc:2169 msgid "Simulate Painting" msgstr "Schilderen Nabootsen" #: f.effects.cc:2173 msgid "color depth" msgstr "kleurdiepte" #: f.effects.cc:2177 msgid "patch area goal" msgstr "vlekgrootte doel" #: f.effects.cc:2181 msgid "req. color match" msgstr "vereiste kleur aanpassing" #: f.effects.cc:2185 msgid "borders" msgstr "randen" #: f.effects.cc:2758 f.widgets.cc:493 msgid "Vignette" msgstr "Vignette" #: f.effects.cc:3107 msgid "Add Texture" msgstr "Toevoegen Textuur " #: f.effects.cc:3324 msgid "Background Pattern" msgstr "Achtergrond Patroon" #: f.effects.cc:3328 msgid "Pattern File:" msgstr "Patroon Bestand:" #: f.effects.cc:3333 msgid "Geometry" msgstr "Geometrie" #: f.effects.cc:3334 msgid "Calculate" msgstr "Bereken" #: f.effects.cc:3336 msgid "Zoom" msgstr "Zoom" #: f.effects.cc:3347 f.widgets.cc:495 msgid "Pattern" msgstr "Patroon" #: f.effects.cc:3354 msgid "Overlap" msgstr "Overlap" #: f.effects.cc:3361 msgid "Opacity" msgstr "Ondoorzichtigheid" #: f.effects.cc:3429 msgid "choose pattern tile" msgstr "kies tegel patroon" #: f.effects.cc:3816 msgid "Create Mosaic" msgstr "Maken Mozaiek" #: f.effects.cc:3902 msgid "Tile" msgstr "Tegel" #: f.effects.cc:3910 f.widgets.cc:490 msgid "Tiles" msgstr "Tegels" #: f.effects.cc:3916 msgid "Tile blending" msgstr "Tegel vermenging" #: f.effects.cc:3996 #, c-format msgid "exceeded max. tiles: %d" msgstr "overschreden max. tegels: %d" #: f.effects.cc:4011 #, c-format msgid "only %d tile images found" msgstr "maar %d tegel afbeeldingen gevonden" #: f.effects.cc:4404 f.widgets.cc:497 msgid "Custom Kernel" msgstr "Custom Kernel" #: f.effects.cc:4408 msgid "Kernel size" msgstr "Kernel grootte" #: f.effects.cc:4411 msgid "Divisor" msgstr "Deler" #: f.effects.cc:4414 msgid "Data file" msgstr "Gegevens bestand" #: f.effects.cc:4450 fotoxx-15.11.cc:3440 msgid "Load settings from file" msgstr "Laad Instellingen bestand" #: f.effects.cc:4711 f.widgets.cc:498 msgid "Make Waves" msgstr "Maak Golven" #: f.effects.cc:4718 msgid "wavelength" msgstr "golflengte" #: f.effects.cc:4719 msgid "amplitude" msgstr "amplitude" #: f.effects.cc:4720 msgid "variance" msgstr "" #: f.effects.cc:4731 msgid "perspective" msgstr "" #: f.effects.cc:4905 msgid "Pull image using the mouse." msgstr "" #: f.effects.cc:4926 f.widgets.cc:499 msgid "Directed Blur" msgstr "" #: f.effects.cc:4930 msgid "blur span" msgstr "" #: f.effects.cc:4933 msgid "intensity" msgstr "" #: f.effects.cc:5099 f.widgets.cc:500 msgid "Spherical Projection" msgstr "" #: f.effects.cc:5143 msgid "Magnify" msgstr "" #: f.file.cc:186 msgid "" "use EXIF photo date or \n" " file modification date" msgstr "" #: f.file.cc:203 f.widgets.cc:572 msgid "File" msgstr "Bestand" #: f.file.cc:357 msgid "UFraw not installed" msgstr "UFraw niet geinstalleerd" #: f.file.cc:373 f.widgets.cc:691 msgid "Open RAW file (ufraw)" msgstr "Open RAW bestand (ufraw)" #: f.file.cc:378 f.file.cc:449 msgid "RAW type not registered in User Settings" msgstr "RAW type niet vastgelegd in Instellingen" #: f.file.cc:444 f.widgets.cc:692 msgid "Open RAW file (Raw Therapee)" msgstr "Open RAW bestand (Raw Therapee)" #: f.file.cc:541 f.widgets.cc:395 msgid "Open Image File" msgstr "Openen ..." #: f.file.cc:560 msgid "unknown file type" msgstr "onbekend bestandstype" #: f.file.cc:753 msgid "Create Blank Image" msgstr "Lege afbeelding maken" #: f.file.cc:755 msgid "file name" msgstr "bestandsnaam" #: f.file.cc:788 msgid "supply a file name" msgstr "" #: f.file.cc:934 f.widgets.cc:400 msgid "Rename Image File" msgstr "Hernoemen bestand..." #: f.file.cc:941 msgid "Old Name" msgstr "oude naam" #: f.file.cc:951 msgid "previous name" msgstr "vorige naam" #: f.file.cc:952 msgid "add 1" msgstr "toevoegen 1" #: f.file.cc:1120 msgid "Copy Image File" msgstr "Kopieer Afbeelding" #: f.file.cc:1121 msgid "Move Image File" msgstr "Verplaats Afbeeldingsbestand" #: f.file.cc:1164 msgid "new location" msgstr "nieuwe locatie" #: f.file.cc:1236 msgid "new location is not a directory" msgstr "nieuwe locatie is geen map" #: f.file.cc:1276 f.file.cc:1518 f.file.cc:1656 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "verwijderen mislukt: \n" " %s" #: f.file.cc:1363 f.widgets.cc:401 msgid "Trash Image File" msgstr "Afbeeldingsbestand naar prullenbak" #: f.file.cc:1364 f.file.cc:1571 msgid "(automatic step to next image)" msgstr "(automatische stap naar volgende afbeelding)" #: f.file.cc:1432 msgid "" "Linux standard trash does not work. \n" "Desktop trash folder will be created." msgstr "" "Linux standaard prullenbak wordt niet ondersteund. \n" "Desktop prullenbak zal worden aangemaakt." #: f.file.cc:1435 msgid "" "Linux and Desktop trash do not work. \n" "Permanently delete the image file?" msgstr "" "Linux en Desktop prullenbak werken niet. \n" "Afbeeldingsbestand definitief verwijderen?" #: f.file.cc:1438 #, c-format msgid "Cannot create trash folder: %s" msgstr "Kan niet map voor prullenbak maken: %s" #: f.file.cc:1450 msgid "Move read-only file to trash?" msgstr "Alleen-lezen bestand naar prullenbak?" #: f.file.cc:1505 #, c-format msgid "error: %s" msgstr "fout: %s" #: f.file.cc:1534 f.file.cc:1673 f.file.cc:2508 msgid "no more images" msgstr "geen afbeeldingen meer" #: f.file.cc:1570 msgid "Delete Image File - CANNOT BE REVERSED" msgstr "Verwijder Afbeeldingsbestand - NIET OMKEERBAAR" #: f.file.cc:1646 msgid "Delete read-only file?" msgstr "" #: f.file.cc:1891 msgid "Save Image File" msgstr "Bewaar Afbeeldingsbestand" #: f.file.cc:1901 msgid "new version" msgstr "nieuwe versie" #: f.file.cc:1902 msgid "new file" msgstr "nieuw bestand" #: f.file.cc:1903 msgid "replace file" msgstr "bestand vervangen" #: f.file.cc:1910 f.file.cc:2281 msgid "save as new file name or type" msgstr "bewaren als nieuw bestandsnaam of type " #: f.file.cc:1912 msgid "replace old file (OVERWRITE)" msgstr "oud bestand vervangen (OVERSCHRIJVEN)" #: f.file.cc:1950 f.file.cc:2057 msgid "cannot save as RAW type" msgstr "bewaren als RAW type niet mogelijk" #: f.file.cc:2051 msgid "" "Transparency map will be lost.\n" "save to PNG file to retain." msgstr "" #: f.file.cc:2191 msgid "Unable to copy EXIF/IPTC data" msgstr "Kan EXIF/IPTC gegevens niet kopieren" #: f.file.cc:2299 f.file.cc:2302 msgid "make current" msgstr "maak actueel" #: f.file.cc:2300 msgid "(new file becomes current file)" msgstr "" #: f.file.cc:2409 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Bestand overschrijven? \n" " %s" #: f.file.cc:2555 f.widgets.cc:554 msgid "Quick Start" msgstr "Snel Start" #: f.file.cc:2558 f.widgets.cc:555 msgid "User Guide" msgstr "Gebruikershandleiding" #: f.file.cc:2561 f.widgets.cc:556 msgid "User Guide Changes" msgstr "Gebruikershandleiding Veranderingen" #: f.file.cc:2564 f.widgets.cc:558 msgid "README" msgstr "README" #: f.file.cc:2567 f.widgets.cc:557 msgid "Edit Functions Summary" msgstr "Opsomming Bewerkingsfuncties" #: f.file.cc:2570 f.widgets.cc:559 msgid "Change Log" msgstr "Veranderingslogboek" #: f.file.cc:2573 f.widgets.cc:560 msgid "Log File" msgstr "Log Bestand" #: f.file.cc:2576 f.widgets.cc:561 msgid "Translations" msgstr "Vertalingen" #: f.file.cc:2579 f.widgets.cc:562 msgid "Home Page" msgstr "Homepage" #: f.file.cc:2582 f.widgets.cc:563 msgid "About" msgstr "Over fotoxx" #: f.file.cc:2586 f.widgets.cc:584 f.widgets.cc:606 fotoxx.h:1123 msgid "Help" msgstr "Help" #: f.file.cc:2739 f.file.cc:2808 #, c-format msgid "file not found: %s" msgstr "bestand niet gevonden: %s" #: f.file.cc:2745 f.file.cc:2814 #, c-format msgid "file type not supported: %s" msgstr "bestandstype niet ondersteund: %s" #: f.gallery.cc:918 f.gallery.cc:1007 f.widgets.cc:604 msgid "Scroll" msgstr "Scroll" #: f.gallery.cc:922 msgid "Sync" msgstr "" #: f.gallery.cc:932 f.repair.cc:5845 f.repair.cc:5850 fotoxx.h:1144 msgid "Open" msgstr "Openen" #: f.gallery.cc:933 msgid "change directory" msgstr "verander map" #: f.gallery.cc:941 msgid "GoTo" msgstr "GaNaar" #: f.gallery.cc:947 f.widgets.cc:599 msgid "Sort" msgstr "Sorteren" #: f.gallery.cc:953 f.gallery.cc:1517 f.gallery.cc:1518 f.gallery.cc:1520 #: f.widgets.cc:597 msgid "Zoom+" msgstr "Zoom+" #: f.gallery.cc:963 f.gallery.cc:1519 f.gallery.cc:1521 f.widgets.cc:598 msgid "Zoom-" msgstr "Zoom-" #: f.gallery.cc:975 f.gallery.cc:1525 msgid "Row↑" msgstr "Rij↑" #: f.gallery.cc:983 f.gallery.cc:1526 msgid "Row↓" msgstr "Rij↓" #: f.gallery.cc:991 f.gallery.cc:1523 f.gallery.cc:1530 f.widgets.cc:602 msgid "Page↑" msgstr "Pagina↑" #: f.gallery.cc:999 f.gallery.cc:1524 f.gallery.cc:1531 f.widgets.cc:603 msgid "Page↓" msgstr "Pagina↓" #: f.gallery.cc:1013 f.gallery.cc:1528 f.widgets.cc:600 msgid "First" msgstr "Eerste" #: f.gallery.cc:1014 f.gallery.cc:1529 f.widgets.cc:601 msgid "Last" msgstr "Laatste" #: f.gallery.cc:1127 msgid "Choose image directory" msgstr "Kies afbeeldingsmap" #: f.gallery.cc:1134 f.gallery.cc:1148 msgid "recent" msgstr "recent" #: f.gallery.cc:1135 f.gallery.cc:1153 msgid "newest" msgstr "nieuwste" #: f.gallery.cc:1217 msgid "no albums found" msgstr "" #: f.gallery.cc:1224 msgid "Choose album" msgstr "" #: f.gallery.cc:1274 msgid "Gallery Sort" msgstr "Galerie Sortering " #: f.gallery.cc:1278 msgid "File Name" msgstr "Bestandsnaam" #: f.gallery.cc:1279 msgid "File Mod Date/Time" msgstr "Bestand wijz datum/tijd" #: f.gallery.cc:1280 msgid "Photo Date/Time (EXIF)" msgstr "Foto Datum/Tijd (EXIF)" #: f.gallery.cc:1282 msgid "ascending" msgstr "oplopend" #: f.gallery.cc:1283 msgid "descending" msgstr "aflopend" #: f.gallery.cc:2212 fotoxx.h:1169 msgid "Select Files" msgstr "Selecteer bestanden" #: f.gallery.cc:2672 msgid "Click list position. Click thumbnail to add." msgstr "Klik regel in lijst. Om toe te voegen klik miniatuur." #: f.gallery.cc:2698 f.gallery.cc:2946 msgid "Edit Bookmarks" msgstr "Bewerken bladwijzers" #: f.gallery.cc:2873 msgid "unable to save bookmarks file" msgstr "kan bladwijzer bestand niet bewaren" #: f.gallery.cc:2946 msgid "Go To Bookmark" msgstr "Ga Naar Bladwijzer" #: f.gallery.cc:3003 f.meta.cc:2844 f.repair.cc:7473 msgid "file not found" msgstr "bestand niet gevonden" #: f.mashup.cc:170 msgid "Mashup layout and background image" msgstr "Mashup layout en achtergrond afbeelding" #: f.mashup.cc:213 msgid "choose an image file" msgstr "kies een afbeeldingsbestand" #: f.mashup.cc:214 msgid "use current image file" msgstr "gebruik huidig afbeeldingsbestand" #: f.mashup.cc:215 msgid "specify layout size and color" msgstr "specificeer layout grootte en kleur" #: f.mashup.cc:216 msgid "open a Mashup project file" msgstr "open een Mashup project bestand" #: f.mashup.cc:241 msgid "choose layout file" msgstr "kies layout bestand" #: f.mashup.cc:259 msgid "no current file" msgstr "" #: f.mashup.cc:291 msgid "Make Layout Image" msgstr "" #: f.mashup.cc:293 msgid "project name" msgstr "" #: f.mashup.cc:319 msgid "supply a project name" msgstr "" #: f.mashup.cc:417 msgid "Edit Images" msgstr "Bewerken Afbeeldingen" #: f.mashup.cc:418 f.mashup.cc:2432 msgid "Edit Text" msgstr "Bewerken Tekst" #: f.mashup.cc:419 msgid "Edit Line" msgstr "Bewerken Lijn" #: f.mashup.cc:420 msgid "Rescale" msgstr "" #: f.mashup.cc:423 msgid "add or edit images" msgstr "toevoegen of bewerken afbeeldingen " #: f.mashup.cc:424 msgid "add or edit text" msgstr "toevoegen of bewerken tekst" #: f.mashup.cc:425 msgid "add or edit lines/arrows" msgstr "toevoegen of bewerken lijnen/pijlen" #: f.mashup.cc:426 msgid "change project scale" msgstr "" #: f.mashup.cc:427 msgid "project complete" msgstr "project gereed" #: f.mashup.cc:428 msgid "cancel project" msgstr "annuleren project" #: f.mashup.cc:446 msgid "rescale project" msgstr "" #: f.mashup.cc:501 msgid "save Mashup output file" msgstr "" #: f.mashup.cc:512 msgid "save Mashup project file?" msgstr "" #: f.mashup.cc:524 msgid "delete Mashup project file?" msgstr "" #: f.mashup.cc:583 msgid "Open Project" msgstr "Open Project" #: f.mashup.cc:612 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "layout afbeeldingsbestand ontbreekt: \n" " %s" #: f.mashup.cc:669 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "overlay afbeeldingsbestand ontbreekt: \n" " %s" #: f.mashup.cc:974 msgid "project file is defective" msgstr "project bestand is beschadigd" #: f.mashup.cc:1004 msgid "Save Project" msgstr "Bewaren Project" #: f.mashup.cc:1145 msgid "layout exceeds 2 gigabytes" msgstr "" #: f.mashup.cc:1219 msgid "Click image to select, drag image to move." msgstr "Klik afbeelding om te selecteren, sleep afbeelding om te verplaatsen" #: f.mashup.cc:1220 msgid "Make black margins transparent" msgstr "" #: f.mashup.cc:1259 msgid "Current image:" msgstr "" #: f.mashup.cc:1263 msgid "Cycle through images:" msgstr "" #: f.mashup.cc:1270 msgid "Scale" msgstr "Schaal" #: f.mashup.cc:1279 msgid "Stacking Order" msgstr "Stapel Volgorde" #: f.mashup.cc:1280 msgid "raise" msgstr "verhogen" #: f.mashup.cc:1281 msgid "lower" msgstr "lager" #: f.mashup.cc:1284 msgid "Base Transparency" msgstr "Basis Doorzichtigheid" #: f.mashup.cc:1288 msgid "Var. Transparency" msgstr "Var. Doorzichtigheid" #: f.mashup.cc:1289 msgid "Paint" msgstr "" #: f.mashup.cc:1292 msgid "Bend and fine-align" msgstr "" #: f.mashup.cc:1293 msgid "Warp" msgstr "" #: f.mashup.cc:1302 msgid "Margins" msgstr "" #: f.mashup.cc:1303 msgid "Hard" msgstr "" #: f.mashup.cc:1304 f.repair.cc:5441 msgid "Blend" msgstr "Mengen" #: f.mashup.cc:1318 msgid "add images to layout" msgstr "toevoegen afbeeldingen aan layout" #: f.mashup.cc:1557 msgid "Paint Image Transparencies" msgstr "Schilderen Afbeeldings Doorzichtigheid" #: f.mashup.cc:1575 msgid "Gradual" msgstr "Geleidelijk" #: f.mashup.cc:1580 fotoxx.h:1148 msgid "Power" msgstr "Sterkte" #: f.mashup.cc:1814 msgid "Pull on the image with the mouse." msgstr "" #: f.mashup.cc:1830 msgid "Warp Image" msgstr "" #: f.mashup.cc:2263 #, c-format msgid "exceeded %d images" msgstr "%d afbeeldingen overschreden" #: f.mashup.cc:2405 msgid "Enter text, [Add] to layout, edit properties." msgstr "" #: f.mashup.cc:2485 msgid "Text File:" msgstr "Textbestand:" #: f.mashup.cc:2489 msgid "add entered text to layout" msgstr "toevoegen ingevoerde tekst aan layout " #: f.mashup.cc:2618 msgid "click position to add text" msgstr "" #: f.mashup.cc:2624 #, c-format msgid "exceeded %d text entries" msgstr "%d tekst entries overschreden" #: f.mashup.cc:2747 msgid "Set line properties, [Add] to layout, edit." msgstr "" #: f.mashup.cc:2771 msgid "Edit Line/Arrow" msgstr "Bewerken Lijn/Pijl" #: f.mashup.cc:2826 msgid "add line/arrow to layout" msgstr "toevoegen lijn/pijl aan layout" #: f.mashup.cc:2919 msgid "click position to add line" msgstr "" #: f.mashup.cc:2925 #, c-format msgid "exceeded %d line entries" msgstr "overschreden %d lijn entries" #: f.meta.cc:180 f.widgets.cc:669 msgid "View Metadata" msgstr "Bekijken Metadata" #: f.meta.cc:430 f.meta.cc:1807 f.widgets.cc:412 f.widgets.cc:670 msgid "Edit Metadata" msgstr "Bewerken Metadata" #: f.meta.cc:433 f.meta.cc:3819 msgid "save metadata to file" msgstr "" #: f.meta.cc:442 msgid "Image Date" msgstr "Datum Afbeelding" #: f.meta.cc:445 msgid "Time" msgstr "Tijd" #: f.meta.cc:451 msgid "Rating (stars):" msgstr "" #: f.meta.cc:463 msgid "Caption" msgstr "Onderschrift" #: f.meta.cc:469 msgid "Comments" msgstr "Commentaar" #: f.meta.cc:477 msgid "Enter New Tag" msgstr "" #: f.meta.cc:484 f.meta.cc:5003 msgid "Matching Tags" msgstr "" #: f.meta.cc:492 msgid "Image Tags" msgstr "Afbeeldings Tags" #: f.meta.cc:498 msgid "Recent Tags" msgstr "recente tags" #: f.meta.cc:507 f.meta.cc:2029 f.meta.cc:5010 msgid "Defined Tags Category" msgstr "" #: f.meta.cc:898 msgid "date format is YYYY-MM-DD" msgstr "datum formaat is YYYY-MM-DD" #: f.meta.cc:902 msgid "date is invalid" msgstr "datum is ongeldig" #: f.meta.cc:936 msgid "time format is HH:MM [:SS]" msgstr "tijd formaat is HH:MM [:SS]" #: f.meta.cc:940 msgid "time is invalid" msgstr "tijd is ongeldig" #: f.meta.cc:1025 fotoxx.h:1133 msgid "Manage Tags" msgstr "Tags beheren" #: f.meta.cc:1025 msgid "orphan tags" msgstr "verweesde tags" #: f.meta.cc:1029 msgid "category" msgstr "categorie" #: f.meta.cc:1032 msgid "tag" msgstr "tag" #: f.meta.cc:1039 msgid "Defined Tags:" msgstr "gedefineerde tags:" #: f.meta.cc:1811 f.meta.cc:2474 msgid "key name" msgstr "sleutel naam" #: f.meta.cc:1813 f.meta.cc:2475 msgid "key value" msgstr "sleutel waarde" #: f.meta.cc:1900 f.widgets.cc:414 msgid "Delete Metadata" msgstr "Verwijderen Metadata" #: f.meta.cc:1902 fotoxx.h:1082 msgid "All" msgstr "Alles" #: f.meta.cc:1903 msgid "One Key:" msgstr "Een Sleutel:" #: f.meta.cc:2006 f.widgets.cc:415 f.widgets.cc:540 msgid "Batch Add/Remove Tags" msgstr "Batchgewijs Toevoegen/Verwijderen Tags" #: f.meta.cc:2017 msgid "tags to add" msgstr "tags om toe te voegen" #: f.meta.cc:2018 msgid "tags to remove" msgstr "tags om te verwijderen" #: f.meta.cc:2102 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " te veel tags" #: f.meta.cc:2217 msgid "specify files and tags" msgstr "specificeer bestanden en tags" #: f.meta.cc:2277 msgid "tag names file" msgstr "" #: f.meta.cc:2349 #, c-format msgid "" "%d tags to rename \n" "in %d image files. \n" "Proceed?" msgstr "" #: f.meta.cc:2461 msgid "Batch Metadata" msgstr "" #: f.meta.cc:2468 msgid "Short List" msgstr "" #: f.meta.cc:2469 msgid "Full List" msgstr "" #: f.meta.cc:2511 msgid "enter key names" msgstr "" #: f.meta.cc:2517 fotoxx.h:1141 msgid "no files selected" msgstr "geen bestanden geselecteerd" #: f.meta.cc:2643 msgid "" "The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names" msgstr "" #: f.meta.cc:3276 f.meta.cc:4011 f.meta.cc:4277 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "verkeerde breedtegraad/lengtegraad: %s %s" #: f.meta.cc:3320 f.meta.cc:3682 f.meta.cc:4486 f.meta.cc:4894 msgid "-noindex in use, disabled" msgstr "" #: f.meta.cc:3336 msgid "choose map file" msgstr "" #: f.meta.cc:3444 msgid "" "fotoxx-maps package not installed \n" "(see http://kornelix.com/packages and /tarballs)" msgstr "" #: f.meta.cc:3449 #, c-format msgid "map file %s is missing" msgstr "" #: f.meta.cc:3453 #, c-format msgid "" "map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f" msgstr "" #: f.meta.cc:3731 msgid "No matching images found" msgstr "Geen matchende afbeeldingen gevonden" #: f.meta.cc:3771 msgid "search range (km)" msgstr "zoekbereik (km)" #: f.meta.cc:3804 f.widgets.cc:418 f.widgets.cc:671 msgid "Edit Geotags" msgstr "Bewerken Geotags" #: f.meta.cc:3805 msgid "Geocoding web service courtesy of" msgstr "Geocoding web service dienstbaarheid van" #: f.meta.cc:3821 f.meta.cc:4060 f.meta.cc:5783 msgid "city" msgstr "stad" #: f.meta.cc:3824 f.meta.cc:4063 f.meta.cc:5786 msgid "country" msgstr "land" #: f.meta.cc:3906 f.meta.cc:4194 msgid "city not found" msgstr "stad niet gevonden" #: f.meta.cc:4027 f.widgets.cc:419 f.widgets.cc:543 msgid "Batch Add Geotags" msgstr "Batchgewijs Toevoegen Geotags" #: f.meta.cc:4256 msgid "" "data is incomplete \n" " proceed?" msgstr "" "gegevens zijn onvolledig \n" " doorgaan?" #: f.meta.cc:4313 msgid "choose city" msgstr "kies stad" #: f.meta.cc:4400 msgid "not found" msgstr "niet gevonden" #: f.meta.cc:4401 msgid "city and country required" msgstr "stad en land verplicht" #: f.meta.cc:4501 msgid "Report Geotag Groups" msgstr "Rapport Geotag Groepen" #: f.meta.cc:4502 msgid "Group by country" msgstr "Groepeer per land" #: f.meta.cc:4503 msgid "Group by country/city" msgstr "Groepeer per land/stad" #: f.meta.cc:4504 msgid "Group by country/city/date" msgstr "Groepeer per land/stad/datum" #: f.meta.cc:4507 msgid "Combine within" msgstr "Samenvoegen binnen" #: f.meta.cc:4509 msgid "days" msgstr "dagen" #: f.meta.cc:4625 msgid "geotag groups" msgstr "geotag groepen" #: f.meta.cc:4931 msgid "Search Image Metadata" msgstr "Doorzoeken Afbeelding Metadata" #: f.meta.cc:4935 msgid "images to search:" msgstr "te zoeken afbeeldingen:" #: f.meta.cc:4936 msgid "all" msgstr "alles" #: f.meta.cc:4937 msgid "current set only" msgstr "alleen huidige verzameling " #: f.meta.cc:4940 msgid "matching images:" msgstr "matchende afbeeldingen:" #: f.meta.cc:4941 msgid "new set" msgstr "nieuwe verzameling" #: f.meta.cc:4942 msgid "add to set" msgstr "toevoegen aan verzameling" #: f.meta.cc:4943 msgid "remove" msgstr "verwijderen" #: f.meta.cc:4946 msgid "report type:" msgstr "rapport type:" #: f.meta.cc:4947 msgid "gallery" msgstr "galerie" #: f.meta.cc:4948 fotoxx-15.11.cc:791 msgid "metadata" msgstr "metadata" #: f.meta.cc:4954 msgid "date range" msgstr "datum van/tot" #: f.meta.cc:4955 msgid "stars range" msgstr "sterren van/tot" #: f.meta.cc:4956 msgid "search tags" msgstr "doorzoeken tags" #: f.meta.cc:4957 msgid "search text" msgstr "doorzoeken tekst" #: f.meta.cc:4958 msgid "search files" msgstr "doorzoeken bestanden" #: f.meta.cc:4963 msgid "(yyyymmdd)" msgstr "(jjjjmmmdd)" #: f.meta.cc:4968 msgid "last version only" msgstr "" #: f.meta.cc:4970 msgid "all/any" msgstr "alle/enkele" #: f.meta.cc:4989 msgid "other criteria" msgstr "andere criteria" #: f.meta.cc:4993 msgid "other" msgstr "ander" #: f.meta.cc:4999 msgid "Enter Search Tag" msgstr "" #: f.meta.cc:5294 #, c-format msgid "not a defined tag: %s" msgstr "" #: f.meta.cc:5325 msgid "" "to remove images from current set, \n" "search current set" msgstr "" "doorzoek huidige verzameling om afbeeldingen \n" "te verwijderen uit huidige verzameling " #: f.meta.cc:5332 msgid "" "to add images to current set, \n" "search all images" msgstr "" "doorzoek alle afbeeldingen om afbeeldingen \n" "toe te voegen aan huidige verzameling " #: f.meta.cc:5376 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" #: f.meta.cc:5394 msgid "stars range not reasonable" msgstr "" #: f.meta.cc:5639 #, c-format msgid "images added: %d removed: %d new count: %d" msgstr "afbeeldingen toegevoegd: %d verwijderd: %d nieuwe telling: %d" #: f.meta.cc:5642 msgid "no changes made" msgstr "geen veranderingen gemaakt" #: f.meta.cc:5780 msgid "Add Geotags Search Criteria" msgstr "Toevoegen Geotags Zoek Criteria" #: f.meta.cc:5799 msgid "range (km)" msgstr "bereik (km)" #: f.meta.cc:5892 msgid "error in latitude/longitude/range" msgstr "fout in breedtegraad/lengtegraad/bereik" #: f.meta.cc:5956 msgid "" "These items are always reported: \n" "date, stars, tags, caption, comment" msgstr "" "Deze items worden altijd gerapporteerd: \n" "datum, sterren, tags, onderschrift, commentaar" #: f.meta.cc:5980 msgid "Additional Items for Report" msgstr "Toegevoegde Items voor Rapport" #: f.meta.cc:5986 msgid "Keyword" msgstr "Sleutelwoord" #: f.meta.cc:5993 msgid "Match Criteria" msgstr "Match Criteria" #: f.meta.cc:6553 msgid "image index is missing" msgstr "afbeeldings index ontbreekt" #: f.repair.cc:1041 msgid "Apply repeatedly while watching the image." msgstr "" #: f.repair.cc:1042 msgid "Measure" msgstr "" #: f.repair.cc:1078 msgid "Noise Reduction" msgstr "Ruis vermindering" #: f.repair.cc:1099 msgid "Flatten 1" msgstr "" #: f.repair.cc:1100 msgid "Flatten 2" msgstr "" #: f.repair.cc:1101 msgid "Median" msgstr "" #: f.repair.cc:1121 msgid "dark areas" msgstr "" #: f.repair.cc:1123 msgid "all areas" msgstr "" #: f.repair.cc:1246 msgid "Measure Noise" msgstr "" #: f.repair.cc:1247 msgid "Click on a monotone image area." msgstr "" #: f.repair.cc:2177 msgid "" "1. Drag mouse to select. \n" "2. Erase. 3. Repeat. " msgstr "" "1. Sleep met de muis om uitsnede te definieren. \n" "2. Druk [verwijderen]. \n" "3. Herhaal met nieuwe uitsnede. " #: f.repair.cc:2199 f.widgets.cc:457 msgid "Smart Erase" msgstr "Slim verwijderen" #: f.repair.cc:2204 fotoxx.h:1152 msgid "Radius" msgstr "straal" #: f.repair.cc:2206 f.widgets.cc:455 msgid "Blur" msgstr "Afbeelding vervagen" #: f.repair.cc:2209 msgid "New Area" msgstr "Nieuwe uitsnede" #: f.repair.cc:2558 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" " Linkerklik in het rode oog om te corrigeren.\n" "Methode 2:\n" " Sleep met de muis naar rechts om het rode oog te markeren.\n" " Linkerklik in het rode oog om te corrigeren.\n" "Ongedaan maken:\n" " Rechterklik in het rode oog." #: f.repair.cc:2574 msgid "Red Eye Reduction" msgstr "Rode ogen correctie" #: f.repair.cc:3038 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 "" "shift + linkerklik: kies kleur of afbeeldings positie \n" "linkerklik of sleep: verf kleur of copieer afbeelding \n" "rechterklik of sleep: verwijder kleur of afbeelding" #: f.repair.cc:3070 f.widgets.cc:459 msgid "Paint/Clone" msgstr "Pixels Schilderen/Klonen" #: f.repair.cc:3074 msgid "paint color" msgstr "schilder kleur" #: f.repair.cc:3077 msgid "copy from image" msgstr "copieer van afbeelding" #: f.repair.cc:3084 f.repair.cc:3641 msgid "paintbrush radius" msgstr "straal penseelcirkel" #: f.repair.cc:3085 msgid "transparency center" msgstr "doorzichtigheid midden" #: f.repair.cc:3086 msgid "transparency edge" msgstr "doorzichtigheid rand" #: f.repair.cc:3093 f.repair.cc:3648 msgid "gradual paint" msgstr "gradueel schilderen" #: f.repair.cc:3100 #, c-format msgid "Undo Memory %d%c" msgstr "Geheugen voor ongedaan maken %d%c" #: f.repair.cc:3436 msgid "" "Undo memory limit has been reached. \n" "Save work with [done], then resume painting." msgstr "" "Herstel geheugen limiet is bereikt \n" "Bewaar werk met [Klaar], dan schilderen hervatten. " #: f.repair.cc:3601 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" #: f.repair.cc:3633 f.widgets.cc:460 msgid "Paint Transparency" msgstr "" #: f.repair.cc:3642 msgid "strength center" msgstr "" #: f.repair.cc:3643 msgid "strength edge" msgstr "" #: f.repair.cc:3858 f.widgets.cc:461 msgid "Color Mode" msgstr "Kleur Mode" #: f.repair.cc:3862 msgid "black/white positive" msgstr "zwart/wit positief" #: f.repair.cc:3863 msgid "black/white negative" msgstr "zwart/wit negatief" #: f.repair.cc:3864 msgid "color positive" msgstr "kleur positief" #: f.repair.cc:3865 msgid "color negative" msgstr "kleur negatief" #: f.repair.cc:3866 msgid "sepia" msgstr "sepia" #: f.repair.cc:4046 f.widgets.cc:462 msgid "Shift Colors" msgstr "Verschuiven Kleuren" #: f.repair.cc:4317 msgid "+Brightness" msgstr "+Helderheid" #: f.repair.cc:4318 msgid "+Red -Cyan" msgstr "+Rood -Cyaan" #: f.repair.cc:4319 msgid "+Green -Magenta" msgstr "+Groen -Magenta" #: f.repair.cc:4320 msgid "+Blue -Yellow" msgstr "+Blauw -Geel" #: f.repair.cc:4326 fotoxx.h:1154 msgid "Red" msgstr "Rood" #: f.repair.cc:4327 fotoxx.h:1120 msgid "Green" msgstr "Groen" #: f.repair.cc:4328 fotoxx.h:1089 msgid "Blue" msgstr "Blauw" #: f.repair.cc:4623 msgid "Color to change" msgstr "" #: f.repair.cc:4625 msgid "shift+click on image to select color" msgstr "" #: f.repair.cc:4638 msgid "Color Hue" msgstr "" #: f.repair.cc:4639 msgid "Saturation" msgstr "" #: f.repair.cc:4640 msgid "Lightness" msgstr "" #: f.repair.cc:4641 msgid "Color Match" msgstr "" #: f.repair.cc:4642 msgid "Adjustment" msgstr "" #: f.repair.cc:5028 msgid "Ramp brightness across image" msgstr "Helderheidsverspreiding horizontaal/verticaal" #: f.repair.cc:5352 f.tools.cc:2048 msgid "Click image to select pixels." msgstr "Klik in afbeelding om pixels te selecteren" #: f.repair.cc:5383 f.widgets.cc:466 msgid "Color Ramp" msgstr "" #: f.repair.cc:5394 msgid "Metric:" msgstr "Metriek:" #: f.repair.cc:5817 msgid "Color Match Images" msgstr "Kleuren Match Afbeeldingen" #: f.repair.cc:5843 msgid "mouse radius for color sample" msgstr "straal cirkel voor kleurmonster" #: f.repair.cc:5846 msgid "image for source color" msgstr "afbeelding voor kleur-selectie" #: f.repair.cc:5848 msgid "click on image to get source color" msgstr "klik in afbeelding voor kleur-selectie" #: f.repair.cc:5851 msgid "image to set matching color" msgstr "afbeelding om matchende kleur te veranderen" #: f.repair.cc:5853 msgid "click on image to set matching color" msgstr "klik op afbeelding om matchende kleur te veranderen" #: f.repair.cc:5914 msgid "select source image color first" msgstr "kies eerst kleur in afbeelding" #: f.repair.cc:6099 msgid "Change Color Profile" msgstr "Wijzigen Kleurprofiel" #: f.repair.cc:6103 msgid "input profile" msgstr "input profiel" #: f.repair.cc:6107 msgid "output profile" msgstr "output profiel" #: f.repair.cc:6123 msgid "color profile" msgstr "kleurprofiel" #: f.repair.cc:6169 f.repair.cc:6175 #, c-format msgid "unknown cms profile %s" msgstr "onbekend cms profiel %s" #: f.repair.cc:6307 f.widgets.cc:469 msgid "Remove Dust" msgstr "Stofspikkels verwijderen" #: f.repair.cc:6311 msgid "spot size limit" msgstr "stip grootte limiet" #: f.repair.cc:6314 msgid "max. brightness" msgstr "max. helderheid" #: f.repair.cc:6317 msgid "min. contrast" msgstr "min. contrast" #: f.repair.cc:7098 msgid "" " Adjust each RGB color to minimize \n" " color fringes at the image extremes. " msgstr "" "Instellen van elke RGB om kleur-franjes op \n" "de afbeeldings-extremen te verminderen " #: f.repair.cc:7121 f.widgets.cc:471 msgid "Color Fringes" msgstr "" #: f.repair.cc:7320 f.widgets.cc:472 msgid "Stuck Pixels" msgstr "" #: f.repair.cc:7326 msgid "pixel group" msgstr "pixel groep" #: f.repair.cc:7334 f.repair.cc:7402 msgid "stuck pixels:" msgstr "defecte pixels:" #: f.repair.cc:7430 msgid "Load Stuck Pixels" msgstr "Laden Defecte Pixels" #: f.repair.cc:7432 msgid "File:" msgstr "Bestand:" #: f.repair.cc:7457 f.repair.cc:7514 msgid "Stuck Pixels file" msgstr "Defecte Pixels bestand" #: f.repair.cc:7494 msgid "file format error" msgstr "fout in bestandsformaat" #: f.tools.cc:70 msgid "" "Select directories containing image files \n" "(subdirectories are included automatically)." msgstr "" #: f.tools.cc:72 msgid "Select to add, click on X to delete." msgstr "" #: f.tools.cc:73 msgid "Select directory for thumbnails." msgstr "" #: f.tools.cc:74 msgid "" "Index function terminated. Fotoxx will close. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" #: f.tools.cc:106 f.widgets.cc:513 msgid "Index Image Files" msgstr "Indexering Afbeeldingen" #: f.tools.cc:250 msgid "Choose top image directories" msgstr "Selecteer hoogste afbeeldingsmap" #: f.tools.cc:251 msgid "Choose thumbnail directory" msgstr "Kies miniaturen map" #: f.tools.cc:324 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 "" #: f.tools.cc:330 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" #: f.tools.cc:333 #, c-format msgid "" "Thumbnails directory: \n" " %s \n" "must be named .../thumbnails" msgstr "" #: f.tools.cc:336 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" #: f.tools.cc:385 msgid "no thumbnails directory defined" msgstr "geen miniaturen map gedefinieerd" #: f.tools.cc:735 msgid "COMPLETED" msgstr "VOLTOOID" #: f.tools.cc:776 msgid "Indexing is required for first-time startup." msgstr "" #: f.tools.cc:838 msgid "Recent Files Gallery" msgstr "Laatst bekeken galerie" #: f.tools.cc:839 msgid "Newest Files Gallery" msgstr "Nieuwste Bestanden Galerie" #: f.tools.cc:840 msgid "Previous Gallery" msgstr "Vorige Galerie" #: f.tools.cc:841 msgid "Previous Image" msgstr "Vorige Afbeelding" #: f.tools.cc:842 msgid "Blank Window" msgstr "Leeg Venster" #: f.tools.cc:843 msgid "Directory" msgstr "" #: f.tools.cc:844 msgid "Image File" msgstr "Afbeeldingsbestand" #: f.tools.cc:887 f.widgets.cc:514 msgid "User Options" msgstr "" #: f.tools.cc:890 msgid "Startup Display" msgstr "Tonen bij Opstart" #: f.tools.cc:901 msgid "Menu Style" msgstr "Menu Stijl" #: f.tools.cc:902 msgid "Icons" msgstr "Pictogrammen" #: f.tools.cc:903 msgid "Icons + Text" msgstr "Pictogrammen + Tekst" #: f.tools.cc:905 msgid "Icon size" msgstr "Pictogram grootte" #: f.tools.cc:909 msgid "Dialog font and size" msgstr "" #: f.tools.cc:914 msgid "Image Pan/scroll:" msgstr "" #: f.tools.cc:917 msgid "Zooms for 2x" msgstr "Zooms voor 2x" #: f.tools.cc:926 msgid "JPEG save quality" msgstr "JPEG kwaliteit" #: f.tools.cc:933 msgid "Curve node capture distance" msgstr "" #: f.tools.cc:937 msgid "show hidden directories in gallery view" msgstr "" #: f.tools.cc:940 msgid "prev/next shows last file version only" msgstr "" #: f.tools.cc:943 msgid "RAW command" msgstr "RAW opdracht" #: f.tools.cc:947 msgid "RAW file types" msgstr "RAW bestandstype" #: f.tools.cc:1029 msgid "Delete present thumbnails to make effective" msgstr "" #: f.tools.cc:1051 msgid "Select startup directory" msgstr "Kies opstart map" #: f.tools.cc:1058 msgid "Select startup image file" msgstr "Kies opstart afbeeldingsbestand" #: f.tools.cc:1096 msgid "startup directory is invalid" msgstr "opstart map is ongeldig" #: f.tools.cc:1107 msgid "startup file is invalid" msgstr "opstart bestand is ongeldig" #: f.tools.cc:1247 msgid "Edit KB Shortcuts" msgstr "Bewerk KB Shortcuts" #: f.tools.cc:1252 msgid "shortcut key:" msgstr "shortcut toets:" #: f.tools.cc:1253 msgid "(enter key)" msgstr "(enter toets)" #: f.tools.cc:1379 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Gereserveerd, kan niet gebruikt worden" #: f.tools.cc:1533 msgid "unable to save KB-shortcuts file" msgstr "kan KB-shortcuts bestand niet bewaren" #: f.tools.cc:1837 f.widgets.cc:517 msgid "Grid Lines" msgstr "Raster instellen" #: f.tools.cc:1846 msgid "x-spacing" msgstr "x-afstand" #: f.tools.cc:1847 msgid "x-count" msgstr "x-aantal" #: f.tools.cc:1848 msgid "x-enable" msgstr "x-activeren" #: f.tools.cc:1854 msgid "y-spacing" msgstr "y-afstand" #: f.tools.cc:1855 msgid "y-count" msgstr "y-aantal" #: f.tools.cc:1856 msgid "y-enable" msgstr "y-activeren" #: f.tools.cc:1976 f.tools.cc:1981 f.widgets.cc:518 msgid "Line Color" msgstr "Lijn kleur" #: f.tools.cc:1978 msgid "Area Color" msgstr "Uitsnede kleur" #: f.tools.cc:2083 f.widgets.cc:519 msgid "Show RGB" msgstr "RGB waarden tonen" #: f.tools.cc:2375 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key M to toggle dialog." msgstr "" #: f.tools.cc:2401 f.widgets.cc:520 msgid "Magnify Image" msgstr "" #: f.tools.cc:2410 msgid "X-size" msgstr "" #: f.tools.cc:2655 msgid "Darkest and Brightest Pixels" msgstr "Donkerste en Lichtste Pixels" #: f.tools.cc:2678 msgid "Dark Limit" msgstr "Donkere Limiet" #: f.tools.cc:2679 msgid "Bright Limit" msgstr "Lichte Limiet" #: f.tools.cc:2774 msgid "" "Brightness should show a gradual ramp \n" "extending all the way to the edges." msgstr "" "Helderheid moet een geleidelijke verandering laten \n" "zien, zich geheel uitstrekkend naar de randen." #: f.tools.cc:2870 f.widgets.cc:523 msgid "Monitor Gamma" msgstr "Gamma waarde monitor" #: f.tools.cc:2930 msgid "Available Translations" msgstr "Beschikbare vertalingen" #: f.tools.cc:2934 msgid "Set Language" msgstr "Instellen taal" #: f.tools.cc:3071 f.widgets.cc:526 msgid "Calibrate Printer" msgstr "" #: f.tools.cc:3096 msgid "print color chart" msgstr "" #: f.tools.cc:3097 msgid "scan and save color chart" msgstr "" #: f.tools.cc:3098 msgid "align and trim color chart" msgstr "" #: f.tools.cc:3099 msgid "open and process color chart" msgstr "" #: f.tools.cc:3100 msgid "print image with revised colors" msgstr "" #: f.tools.cc:3264 #, c-format msgid "" "Scan the printed color chart. \n" "Save in %s/" msgstr "" #: f.tools.cc:3278 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation caused by the scanner. \n" "Trim off all margins. Be careful not to cut off \n" "any color tile edges or leave any white margins." msgstr "" #: f.tools.cc:3310 msgid "Open the trimmed color chart file" msgstr "" #: f.tools.cc:3443 msgid "" "Set the name for the output calibration file \n" "[your calibration name].dat" msgstr "" #: f.tools.cc:3483 msgid "Color map file to use" msgstr "" #: f.tools.cc:3500 msgid "Select the image file to print." msgstr "" #: f.tools.cc:3565 msgid "converting colors..." msgstr "" #: f.tools.cc:3665 msgid "Image colors are converted for printing." msgstr "" #: f.widgets.cc:111 msgid "Album" msgstr "" #: f.widgets.cc:113 msgid "TOP" msgstr "TOP" #: f.widgets.cc:165 msgid "Current Image File (key F)" msgstr "" #: f.widgets.cc:166 msgid "Thumbnail Gallery (key G)" msgstr "" #: f.widgets.cc:167 msgid "World Maps (key W)" msgstr "" #: f.widgets.cc:170 msgid "Favorite Functions" msgstr "Favoriete Functies" #: f.widgets.cc:171 msgid "File: Open, RAW, Rename, Trash, Print" msgstr "" #: f.widgets.cc:172 msgid "Save modified image file to disk" msgstr "Bewaar gewijzigd afbeeldingsbestand" #: f.widgets.cc:173 msgid "Open previous or next file (left/right mouse click)" msgstr "Open vorig of volgend bestand (linker/rechter muisklik)" #: f.widgets.cc:174 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "Metadata: Onderschriften, Tags, Geotags, Zoeken ..." #: f.widgets.cc:175 msgid "Areas: Select areas to edit, copy and paste" msgstr "Uitsnedes: Selecteer uitsnedes om te wijzigen, copieren en plakken." #: f.widgets.cc:176 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "" "Bewerken: Bijsnijden, Draaien, Herschalen, Helderheid, Contrast, Tekst ..." #: f.widgets.cc:177 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "Repareren: Verscherpen, Ruis, Rode ogen, Kleur, Schilderen, Klonen ..." #: f.widgets.cc:178 msgid "Bend: Fix Perspective, Bend/Warp image ..." msgstr "Krommen: Fix Perspectief, Krommen/Kromtrekken afbeelding ..." #: f.widgets.cc:179 msgid "Effects: Special Effects, Arty Transforms" msgstr "Effecten: Speciale Effecten, Artistieke Transformaties" #: f.widgets.cc:180 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "Samenvoegen: HDR, HDF, Panorama, Stapel, Mashup" #: f.widgets.cc:181 msgid "" "Undo or Redo one edit (left/right mouse click) \n" " hold key A to include all edits" msgstr "" "Herstel of Opnieuw een bewerking (linker/rechter muisklik) \n" " toets A ingedrukt houden om alle bewerkingen mee te nemen " #: f.widgets.cc:183 msgid "Tools: Index, Options, Shortcuts, Magnify ..." msgstr "" #: f.widgets.cc:184 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "Help: Snel Start, Gebruikershandleiding, Recente veranderingen ..." #: f.widgets.cc:187 msgid "Set gallery from current image file" msgstr "Instellen galerie van huidige afbeelding" #: f.widgets.cc:188 msgid "Albums: Manage Albums, Slide Show, Desktop Wallpaper" msgstr "" #: f.widgets.cc:189 msgid "go to bookmarked image" msgstr "ga naar afbeelding van bladwijzer" #: f.widgets.cc:190 msgid "increase thumbnail size" msgstr "vergroten miniaturen" #: f.widgets.cc:191 msgid "reduce thumbnail size" msgstr "verkleinen miniaturen" #: f.widgets.cc:192 msgid "change sort order" msgstr "verander sorteer volgorde" #: f.widgets.cc:193 msgid "jump to beginning (top)" msgstr "spring naar begin (boven)" #: f.widgets.cc:194 msgid "jump to end (bottom)" msgstr "spring naar eind (onder)" #: f.widgets.cc:195 msgid "previous page" msgstr "vorige pagina" #: f.widgets.cc:196 msgid "next page" msgstr "volgende pagina" #: f.widgets.cc:197 msgid "slow scroll" msgstr "langzame scroll" #: f.widgets.cc:198 msgid "Batch conversion, metadata, RAW processing" msgstr "" #: f.widgets.cc:201 msgid "Choose a map for image locations" msgstr "" #: f.widgets.cc:202 msgid "Set image search radius for map click" msgstr "" #: f.widgets.cc:205 msgid "Open another window" msgstr "Open nog een venster" #: f.widgets.cc:206 msgid "Open a new image file" msgstr "Open nieuwe afbeelding" #: f.widgets.cc:207 msgid "Open the previously seen file" msgstr "Open vorig getoond bestand" #: f.widgets.cc:208 msgid "Open a recently seen file" msgstr "Open een onlangs getoond bestand" #: f.widgets.cc:209 msgid "Open a newly added file" msgstr "Open een nieuw toegevoegd bestand" #: f.widgets.cc:210 f.widgets.cc:211 msgid "Open and edit a camera RAW file" msgstr "Open en bewerk een RAW bestand" #: f.widgets.cc:212 msgid "Change the image file name" msgstr "Wijzig naam van het afbeeldingsbestand" #: f.widgets.cc:213 msgid "Create a blank image" msgstr "Maak een lege afbeelding" #: f.widgets.cc:214 msgid "Move image file to Trash" msgstr "Verplaats afbeeldingsbestand naar prullenbak" #: f.widgets.cc:215 msgid "Permanently delete image file" msgstr "Definitief verwijderen afbeeldings bestand" #: f.widgets.cc:216 msgid "Print the current image" msgstr "Afdrukken actuele afbeelding" #: f.widgets.cc:217 msgid "Print current image with adjusted colors" msgstr "" #: f.widgets.cc:218 f.widgets.cc:406 msgid "Quit Fotoxx" msgstr "Verlaat Fotoxx" #: f.widgets.cc:221 msgid "List a few key metadata items" msgstr "Belangrijkste metadata opsommen" #: f.widgets.cc:222 msgid "List all metadata items" msgstr "Opsommen alle metadata items" #: f.widgets.cc:223 msgid "(Toggle) show captions and comments" msgstr "(Wissel) toon onderschriften en commentaar" #: f.widgets.cc:224 msgid "Edit image tags/caption/rating ..." msgstr "Bewerken afbeeldings tags/onderschrift/rating ..." #: f.widgets.cc:225 msgid "Edit any image metadata" msgstr "Bewerken metadata van afbeelding" #: f.widgets.cc:226 msgid "Remove all metadata from an image" msgstr "Verwijder alle metadata van een afbeelding" #: f.widgets.cc:227 msgid "Add/remove tags for multiple images" msgstr "Toevoegen/verwijderen tags voor meerdere afbeeldingen" #: f.widgets.cc:228 msgid "Convert tag names for all images" msgstr "" #: f.widgets.cc:229 msgid "Add/change/delete metadata for multiple images" msgstr "" #: f.widgets.cc:230 msgid "Edit image location and geotags" msgstr "Bewerken afbeeldings locatie en geotags" #: f.widgets.cc:231 msgid "Add/revise geotags for multiple images" msgstr "" #: f.widgets.cc:232 msgid "Find all images for a location [date]" msgstr "Zoek alle afbeeldingen voor een locatie [datum]" #: f.widgets.cc:233 msgid "Find images meeting select criteria" msgstr "Zoek afbeeldingen volgens selectie criteria" #: f.widgets.cc:236 msgid "Select object or area for editing" msgstr "Selecteer object of uitsnede om te bewerken" #: f.widgets.cc:237 msgid "Show (outline) existing area" msgstr "Toon (contour) bestaande uitsnede" #: f.widgets.cc:238 msgid "Hide existing area" msgstr "Verberg bestaande uitsnede" #: f.widgets.cc:239 msgid "Enable area for editing" msgstr "Activeer uitsnede om te bewerken" #: f.widgets.cc:240 msgid "Disable area for editing" msgstr "Deactiveer uitsnede om te bewerken" #: f.widgets.cc:241 msgid "Reverse existing area" msgstr "Bestaande uitsnede omkeren" #: f.widgets.cc:242 msgid "Erase existing area" msgstr "Verwijder bestaande uitsnede" #: f.widgets.cc:243 msgid "Copy area for later pasting into image" msgstr "" #: f.widgets.cc:244 msgid "Paste previously copied area into image" msgstr "" #: f.widgets.cc:245 msgid "Open a file and paste as area into image" msgstr "" #: f.widgets.cc:246 msgid "Save area to a file with transparency" msgstr "" #: f.widgets.cc:249 msgid "Trim/Crop margins and/or Rotate" msgstr "Bijsnijden marges en/of Draaien" #: f.widgets.cc:250 msgid "Upright a rotated image" msgstr "" #: f.widgets.cc:251 f.widgets.cc:252 msgid "Fast auto enhance that may work OK" msgstr "Snelle auto-verbetering dat mogelijk goed werkt" #: f.widgets.cc:253 msgid "Adjust brightness, contrast, color" msgstr "Instellen helderheid, contrast, kleur" #: f.widgets.cc:254 msgid "Add local contrast, enhance details" msgstr "Toevoegen lokaal contrast, verbeter details" #: f.widgets.cc:255 msgid "Adjust brightness distribution" msgstr "Instellen helderheids verdeling" #: f.widgets.cc:256 msgid "Flatten zonal brightness distribution" msgstr "" #: f.widgets.cc:257 msgid "Change pixel dimensions" msgstr "Verander pixel dimensies" #: f.widgets.cc:258 msgid "Mirror image horizontally or vertically" msgstr "Spiegel afbeelding horizontaal of verticaal" #: f.widgets.cc:259 msgid "Write text on image" msgstr "Schrijf tekst op afbeelding" #: f.widgets.cc:260 msgid "Write lines or arrows on image" msgstr "Schrijf lijnen of pijlen op afbeelding" #: f.widgets.cc:261 msgid "Fix brightness uniformity across image" msgstr "Repareren helderheidsverdeling over de afbeelding" #: f.widgets.cc:262 msgid "Paint edit function gradually with mouse" msgstr "Bewerkingsfunctie geleidelijk met muis schilderen " #: f.widgets.cc:263 msgid "Leverage edits by brightness or color" msgstr "Veranderingen versterken met helderheid of kleur" #: f.widgets.cc:264 msgid "Edit plugins menu or run a plugin function" msgstr "" #: f.widgets.cc:267 msgid "Make the image look sharper" msgstr "Laat de afbeelding er scherper uitzien" #: f.widgets.cc:268 msgid "Make the image look fuzzy" msgstr "Laat de afbeelding er nevelig uitzien" #: f.widgets.cc:269 msgid "Filter noise from low-light photos" msgstr "Filter ruis van laag belichte foto's" #: f.widgets.cc:270 msgid "Remove unwanted objects" msgstr "Verwijder ongewenste objecten" #: f.widgets.cc:271 msgid "Fix red-eyes from electronic flash" msgstr "Repareren rode-ogen door electronisch flitslicht" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Schilderen afbeeldings pixels met de muis" #: f.widgets.cc:273 msgid "Paint image transparency using the mouse" msgstr "" #: f.widgets.cc:274 msgid "Make BW/color, negative/positive, sepia" msgstr "Maak BW/kleur, negatief/positief, sepia" #: f.widgets.cc:275 msgid "Shift/convert colors into other colors" msgstr "Verschuiven/converteren kleuren naar andere kleuren" #: f.widgets.cc:276 msgid "Adjust color using RGB or CMY colors" msgstr "" #: f.widgets.cc:277 msgid "Adjust color using HSL colors" msgstr "" #: f.widgets.cc:278 msgid "Adjust color in selected image areas" msgstr "Instellen kleur in geselecteerde uitsnedes" #: f.widgets.cc:279 msgid "Match colors on one image with another" msgstr "Match kleuren van twee afbeeldingen" #: f.widgets.cc:280 msgid "Convert to another color profile" msgstr "Omzetten naar ander kleurprofiel" #: f.widgets.cc:281 msgid "Remove dust spots from scanned slides" msgstr "Verwijderen stofspikkels van gescande dia's" #: f.widgets.cc:282 msgid "Smoothen edges with jaggies" msgstr "Glad maken randen met randjes" #: f.widgets.cc:283 msgid "Reduce Chromatic Abberation" msgstr "Verminderen Chromatische Aberratie" #: f.widgets.cc:284 msgid "Erase known hot and dark pixels" msgstr "Verwijderen bekende hete en donkere pixels" #: f.widgets.cc:287 msgid "Remove curvature, esp. panoramas" msgstr "Verwijder kromming, met name panorama's" #: f.widgets.cc:288 msgid "Straighten objects seen from an angle" msgstr "Rechttrekken objecten gezien vanuit een hoek" #: f.widgets.cc:289 msgid "Distort image areas using the mouse" msgstr "Verdraai uitsnede met de muis" #: f.widgets.cc:290 f.widgets.cc:291 f.widgets.cc:292 msgid "Distort the whole image using the mouse" msgstr "Verdraai hele afbeelding met de muis" #: f.widgets.cc:293 msgid "Flatten a photographed book page" msgstr "Platstrijken foto van pagina" #: f.widgets.cc:296 msgid "Reduce color depth (posterize)" msgstr "Verminderen kleurdiepte (minder schakeringen)" #: f.widgets.cc:297 msgid "Convert to pencil sketch" msgstr "Converteer naar potlood schets" #: f.widgets.cc:298 msgid "Convert to line drawing (edge detection)" msgstr "Converteren naar lijn tekenen (rand detectie)" #: f.widgets.cc:299 msgid "Convert to solid color drawing" msgstr "Converteer naar effen kleur tekenen" #: f.widgets.cc:300 msgid "Graduated Blur depending on contrast" msgstr "Graduele Vervaging afhankelijk van contrast" #: f.widgets.cc:301 msgid "Create an embossed or 3D appearance" msgstr "Maak een relief of 3D verschijning" #: f.widgets.cc:302 msgid "Convert to square tiles" msgstr "Converteren naar vierkante tegels" #: f.widgets.cc:303 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "Converteren naar stippen (Roy Lichtenstein effect)" #: f.widgets.cc:304 msgid "Convert into a simulated painting" msgstr "Converteren naar nagebootst schilderij" #: f.widgets.cc:305 msgid "Change brightness or color radially" msgstr "Verander helderheid of kleur radiaal" #: f.widgets.cc:306 msgid "Add texture to an image" msgstr "Toevoegen textuur aan afbeelding" #: f.widgets.cc:307 msgid "Tile image with a repeating pattern" msgstr "Tegel afbeelding met een herhaald patroon" #: f.widgets.cc:308 msgid "Create a mosaic with tiles made from all images" msgstr "Creeer een mozaiek met tegels gemaakt van alle afbeeldingen" #: f.widgets.cc:309 msgid "Process an image using a custom kernel" msgstr "Verwerken afbeelding met aangepaste kernel" #: f.widgets.cc:310 msgid "Warp an image with a wave pattern" msgstr "Kromtrekken afbeelding met een golf patroon" #: f.widgets.cc:311 msgid "Blur an image in the direction of mouse drags" msgstr "" #: f.widgets.cc:312 msgid "Make a spherical projection of an image" msgstr "" #: f.widgets.cc:315 msgid "Combine bright/dark images for better detail" msgstr "Samenvoegen licht/donker afbeeldingen voor beter detail" #: f.widgets.cc:316 msgid "Combine near/far focus images for deeper focus" msgstr "Samenvoegen dichtbij/veraf afbeeldingen voor meer scherptediepte" #: f.widgets.cc:317 msgid "Combine images to erase passing people, etc." msgstr "Samenvoegen afbeeldingen om voorbijgangers etc. te verwijderen " #: f.widgets.cc:318 msgid "Combine noisy images into a low-noise image" msgstr "Samenvoegen ruis afbeeldingen naar lagere-ruis afbeelding" #: f.widgets.cc:319 msgid "Combine images into a panorama" msgstr "Samenvoegen afbeeldingen in een panorama" #: f.widgets.cc:320 msgid "Combine images into a vertical panorama" msgstr "Samenvoegen afbeeldingen in een verticaal panorama" #: f.widgets.cc:321 msgid "Combine images into a panorama (panorama tools)" msgstr "Samenvoegen afbeeldingen in een panorama (panorama tools)" #: f.widgets.cc:322 msgid "Arrange images and text in a layout (montage)" msgstr "" #: f.widgets.cc:325 msgid "Index new files and make thumbnails" msgstr "Indexeer nieuwe bestanden en maak miniaturen" #: f.widgets.cc:326 msgid "Change user preferences" msgstr "Verander gebruikersvoorkeuren" #: f.widgets.cc:327 msgid "Change Keyboard Shortcut Keys" msgstr "Wijzig Keyboard Shortcut Toetsen" #: f.widgets.cc:328 msgid "Show a brightness distribution graph" msgstr "Toon helderheids verdeling grafiek" #: f.widgets.cc:329 msgid "Show or revise grid lines" msgstr "Toon of herstel raster lijnen" #: f.widgets.cc:330 msgid "Change color of foreground lines" msgstr "Verander kleur van voorgrond lijnen" #: f.widgets.cc:331 msgid "Show RGB colors at mouse click" msgstr "Toon RGB kleuren bij muisklik" #: f.widgets.cc:332 msgid "Magnify image around the mouse position" msgstr "" #: f.widgets.cc:333 msgid "Highlight darkest and brightest pixels" msgstr "Markeer donkerste en lichtste pixels" #: f.widgets.cc:334 msgid "Chart to adjust monitor color" msgstr "Grafiek instellen monitor kleur" #: f.widgets.cc:335 msgid "Chart to adjust monitor gamma" msgstr "Grafiek instellen monitor gamma" #: f.widgets.cc:336 msgid "Change the GUI language" msgstr "Verander de GUI taal" #: f.widgets.cc:337 msgid "Report missing translations" msgstr "Rapport ontbrekende vertalingen" #: f.widgets.cc:338 msgid "Calibrate printer colors" msgstr "" #: f.widgets.cc:339 msgid "Memory and CPU (to terminal/logfile)" msgstr "Geheugen en CPU (naar beeldscherm/logfile)" #: f.widgets.cc:343 msgid "Rename/convert/resize/move multiple files" msgstr "Hernoemen/Converteren/Herschalen/Verplaatsen meerdere bestanden" #: f.widgets.cc:344 msgid "Upright multiple rotated image files" msgstr "" #: f.widgets.cc:345 msgid "Delete or Trash multiple files" msgstr "" #: f.widgets.cc:346 msgid "Convert camera RAW files using DCraw" msgstr "Converteren camera RAW bestanden met DCraw" #: f.widgets.cc:347 msgid "Convert camera RAW files using Raw Therapee" msgstr "Converteren camera RAW bestanden met Raw Therapee" #: f.widgets.cc:348 msgid "Build and run edit script files" msgstr "" #: f.widgets.cc:349 msgid "Burn selected image files to CD or DVD" msgstr "Brand geselecteerde afbeeldingsbestanden naar CD of DVD" #: f.widgets.cc:350 msgid "Search all image files and report duplicates" msgstr "" #: f.widgets.cc:354 msgid "Quick Start mini-guide" msgstr "Snel Start mini-handleiding" #: f.widgets.cc:355 msgid "Read the user guide" msgstr "Lees de gebruikershandleiding" #: f.widgets.cc:356 msgid "Recent user guide changes" msgstr "Recente veranderingen gebruikershandleiding" #: f.widgets.cc:357 msgid "Summary of image edit functions" msgstr "Opsomming van bewerkingsfuncties voor afbeeldingen" #: f.widgets.cc:358 msgid "Technical installation notes" msgstr "Opmerkingen Technische Installatie" #: f.widgets.cc:359 msgid "List updates by Fotoxx version" msgstr "Opsomming updates per Fotoxx versie" #: f.widgets.cc:360 msgid "View the log file and error messages" msgstr "Bekijk het log bestand en foutboodschappen" #: f.widgets.cc:361 msgid "How to do Fotoxx translations" msgstr "Hoe Fotoxx vertalen" #: f.widgets.cc:362 msgid "Show the Fotoxx web page" msgstr "Toon de Fotoxx webpagina" #: f.widgets.cc:363 msgid "Version, license, contact, credits" msgstr "Versie, licentie, contact, bijdragen" #: f.widgets.cc:366 msgid "Organize images into albums" msgstr "" #: f.widgets.cc:367 msgid "Start a slide show" msgstr "Start een diashow" #: f.widgets.cc:368 msgid "Set desktop wallpaper from current Fotoxx image" msgstr "" #: f.widgets.cc:369 msgid "Cycle desktop wallpaper from a Fotoxx album" msgstr "" #: f.widgets.cc:391 msgid "New Window" msgstr "Nieuw Venster" #: f.widgets.cc:392 msgid "Sync Gallery" msgstr "" #: f.widgets.cc:393 msgid "Recently Seen Images" msgstr "Onlangs Bekeken Afbeeldingen" #: f.widgets.cc:394 msgid "Newest Images" msgstr "Nieuwste Afbeeldingen" #: f.widgets.cc:396 msgid "Open Previous File" msgstr "Open vorig bestand" #: f.widgets.cc:397 msgid "Open RAW (ufraw)" msgstr "" #: f.widgets.cc:398 msgid "Open RAW (Raw Therapee)" msgstr "" #: f.widgets.cc:399 msgid "New Blank Image" msgstr "Nieuwe Lege Afbeelding" #: f.widgets.cc:402 msgid "Delete Image File" msgstr "Verwijder Afbeeldings Bestand" #: f.widgets.cc:403 msgid "Print Image" msgstr "Afdrukken Afbeelding" #: f.widgets.cc:404 msgid "Print Calibrated Image" msgstr "" #: f.widgets.cc:405 f.widgets.cc:550 msgid "Set Desktop Wallpaper" msgstr "" #: f.widgets.cc:409 msgid "View Metadata (short)" msgstr "Tonen Metadata (kort)" #: f.widgets.cc:410 msgid "View Metadata (long)" msgstr "Toon Metadata (lang)" #: f.widgets.cc:411 msgid "Show Captions on Image" msgstr "Toon Onderschriften op Afbeelding" #: f.widgets.cc:413 msgid "Edit Any Metadata" msgstr "Bewerken Metadata" #: f.widgets.cc:416 f.widgets.cc:541 msgid "Batch Rename Tags" msgstr "" #: f.widgets.cc:417 f.widgets.cc:542 msgid "Batch Add/Change Metadata" msgstr "" #: f.widgets.cc:420 f.widgets.cc:544 f.widgets.cc:618 msgid "Images by Geotag" msgstr "" #: f.widgets.cc:421 f.widgets.cc:545 f.widgets.cc:619 msgid "Search Images" msgstr "Afbeeldingen doorzoeken" #: f.widgets.cc:424 fotoxx.h:1168 msgid "Select" msgstr "Selecteren" #: f.widgets.cc:425 fotoxx.h:1170 msgid "Show" msgstr "Tonen" #: f.widgets.cc:426 fotoxx.h:1124 msgid "Hide" msgstr "Verbergen" #: f.widgets.cc:427 fotoxx.h:1110 msgid "Enable" msgstr "Uitsnede activeren" #: f.widgets.cc:428 fotoxx.h:1106 msgid "Disable" msgstr "Uitsnede deactiveren" #: f.widgets.cc:429 fotoxx.h:1127 msgid "Invert" msgstr "Uitsnede inverteren" #: f.widgets.cc:430 fotoxx.h:1184 msgid "Unselect" msgstr "Deselecteren" #: f.widgets.cc:431 msgid "Copy Area" msgstr "" #: f.widgets.cc:432 msgid "Paste Area" msgstr "" #: f.widgets.cc:433 msgid "Open Area File" msgstr "" #: f.widgets.cc:434 msgid "Save Area File" msgstr "" #: f.widgets.cc:439 msgid "Voodoo 1" msgstr "" #: f.widgets.cc:440 msgid "Voodoo 2" msgstr "" #: f.widgets.cc:442 f.widgets.cc:687 msgid "Brightness Dist." msgstr "Helderheids Verdeling" #: f.widgets.cc:443 f.widgets.cc:688 msgid "Zonal Flatten" msgstr "" #: f.widgets.cc:447 msgid "Add Text" msgstr "Schrijf Tekst" #: f.widgets.cc:448 msgid "Add Lines" msgstr "Toevoegen Lijnen" #: f.widgets.cc:451 msgid "Plugins" msgstr "Plugins" #: f.widgets.cc:456 msgid "Denoise" msgstr "Ruis verminderen" #: f.widgets.cc:458 msgid "Red Eyes" msgstr "Rode ogen corrigeren" #: f.widgets.cc:463 msgid "Adjust RGB/CMY" msgstr "" #: f.widgets.cc:464 msgid "Adjust HSL" msgstr "" #: f.widgets.cc:465 msgid "Brightness Ramp" msgstr "Helderheidsverspreiding instellen" #: f.widgets.cc:467 msgid "Match Colors" msgstr "Match Kleuren" #: f.widgets.cc:468 msgid "Color Profile" msgstr "Kleurprofiel" #: f.widgets.cc:470 msgid "Anti-Alias" msgstr "Anti-Alias" #: f.widgets.cc:476 msgid "Fix Perspective" msgstr "Fix Perspectief" #: f.widgets.cc:481 msgid "Flatten Book Page" msgstr "Platstrijken Pagina van Boek" #: f.widgets.cc:484 msgid "Color Depth" msgstr "Kleurdiepte instellen" #: f.widgets.cc:485 msgid "Sketch" msgstr "Schets" #: f.widgets.cc:489 msgid "Embossing" msgstr "Reliefdruk" #: f.widgets.cc:491 msgid "Dots" msgstr "Krantendruk" #: f.widgets.cc:492 msgid "Painting" msgstr "Schilderij" #: f.widgets.cc:494 msgid "Texture" msgstr "Textuur" #: f.widgets.cc:496 msgid "Mosaic" msgstr "Mozaiek" #: f.widgets.cc:503 msgid "High Dynamic Range" msgstr "Hoog dynamisch bereik (HDR)" #: f.widgets.cc:504 msgid "High Depth of Field" msgstr "Hoge scherptediepte (HDF)" #: f.widgets.cc:505 msgid "Stack/Paint" msgstr "Stapelen/Schilderen" #: f.widgets.cc:506 msgid "Stack/Noise" msgstr "Stapelen/Ruis verminderen" #: f.widgets.cc:507 msgid "Panorama" msgstr "Panorama maken" #: f.widgets.cc:508 msgid "Vertical Panorama" msgstr "Verticaal panorama" #: f.widgets.cc:509 msgid "PT Panorama" msgstr "" #: f.widgets.cc:510 msgid "Mashup" msgstr "Mashup" #: f.widgets.cc:515 msgid "Keyboard Shortcuts" msgstr "Keyboard Shortcuts" #: f.widgets.cc:516 msgid "Show Brightness Dist." msgstr "Toon Helderheids Verdeling" #: f.widgets.cc:521 msgid "Dark/Bright Pixels" msgstr "Donker/Licht Pixels" #: f.widgets.cc:522 msgid "Monitor Color" msgstr "Monitor Kleur " #: f.widgets.cc:524 msgid "Change Language" msgstr "Taal veranderen" #: f.widgets.cc:525 msgid "Missing Translations" msgstr "Ontbrekende Vertalingen" #: f.widgets.cc:527 msgid "Resources" msgstr "Hulpbronnen" #: f.widgets.cc:534 msgid "Batch RAW (DCraw)" msgstr "Batchgewijs RAW (DCraw)" #: f.widgets.cc:535 msgid "Batch RAW (Raw Therapee)" msgstr "Batchgewijs RAW (Raw Therapee)" #: f.widgets.cc:536 msgid "Script Files" msgstr "" #: f.widgets.cc:537 msgid "Burn Images to CD/DVD" msgstr "Afbeelding op CD/DVD branden" #: f.widgets.cc:551 msgid "Cycle Desktop Wallpaper" msgstr "" #: f.widgets.cc:568 f.widgets.cc:591 f.widgets.cc:613 msgid "Gallery" msgstr "Galerie" #: f.widgets.cc:569 f.widgets.cc:592 f.widgets.cc:614 msgid "World Maps" msgstr "" #: f.widgets.cc:571 msgid "Favorites" msgstr "Favorieten" #: f.widgets.cc:573 fotoxx.h:1164 msgid "Save" msgstr "Opslaan" #: f.widgets.cc:574 msgid "Prev/Next" msgstr "Vorig/Volgend" #: f.widgets.cc:575 msgid "Metadata" msgstr "Metadata" #: f.widgets.cc:576 msgid "Areas" msgstr "Uitsnedes" #: f.widgets.cc:577 fotoxx.h:1109 msgid "Edit" msgstr "Bewerken" #: f.widgets.cc:578 msgid "Repair" msgstr "Repareren" #: f.widgets.cc:579 msgid "Bend" msgstr "Krommen" #: f.widgets.cc:580 msgid "Effects" msgstr "Effecten" #: f.widgets.cc:581 msgid "Combine" msgstr "Samenvoegen" #: f.widgets.cc:582 msgid "Undo/Redo" msgstr "Herstel/Opnieuw" #: f.widgets.cc:583 msgid "Tools" msgstr "Gereedschap" #: f.widgets.cc:594 msgid "Sync.G" msgstr "Sync.G" #: f.widgets.cc:595 msgid "Albums" msgstr "" #: f.widgets.cc:596 msgid "Bookmarks" msgstr "" #: f.widgets.cc:605 msgid "Batch" msgstr "" #: f.widgets.cc:616 msgid "Choose Map" msgstr "" #: f.widgets.cc:617 msgid "Search Range" msgstr "" #: f.widgets.cc:625 fotoxx.h:1182 msgid "Undo" msgstr "Ongedaan maken" #: f.widgets.cc:626 fotoxx.h:1155 msgid "Redo" msgstr "Opnieuw" #: f.widgets.cc:667 msgid "Popup Image" msgstr "Popup Afbeelding" #: f.widgets.cc:668 msgid "Popup Image (add)" msgstr "" #: f.widgets.cc:672 fotoxx.h:1158 msgid "Rename" msgstr "Hernoemen" #: f.widgets.cc:673 msgid "Copy to Location" msgstr "Kopieren naar Locatie" #: f.widgets.cc:674 msgid "Move to Location" msgstr "Verplaatsen naar Locatie" #: f.widgets.cc:675 msgid "Copy to Clipboard" msgstr "Copieer naar Klembord" #: f.widgets.cc:676 msgid "Remove from Album" msgstr "" #: f.widgets.cc:677 msgid "Cut to Image Cache" msgstr "Verplaatsen naar Afbeeldings Cache" #: f.widgets.cc:678 msgid "Copy to Image Cache" msgstr "Copieer naar Afbeeldings Cache" #: f.widgets.cc:679 msgid "Paste Image Cache Here (clear)" msgstr "Plakken Afbeeldings Cache Hier (schonen)" #: f.widgets.cc:680 msgid "Paste Image Cache Here (keep)" msgstr "Plakken Afbeeldings Cache Hier (bewaren)" #: f.widgets.cc:681 msgid "Paste Current Image File Here" msgstr "" #: f.widgets.cc:685 msgid "Voodoo Enhance" msgstr "Voodoo Verbeteren" #: f.widgets.cc:690 msgid "Select Area" msgstr "Uitsnede" #: f.widgets.cc:693 fotoxx.h:1178 msgid "Trash" msgstr "Prullenbak" #: f.widgets.cc:819 msgid "Image Locations" msgstr "Afbeeldings Locaties" #: fotoxx-15.11.cc:373 msgid "Please install missing programs:" msgstr "Ontbrekende programma's installeren" #: fotoxx-15.11.cc:662 msgid "Kill active dialog?" msgstr "Afsluiten actieve dialoog?" #: fotoxx-15.11.cc:733 msgid "(reduced)" msgstr "(gereduceerd)" #: fotoxx-15.11.cc:734 msgid "area active" msgstr "uitsnede actief" #: fotoxx-15.11.cc:735 msgid "dialog open" msgstr "dialoog open" #: fotoxx-15.11.cc:736 msgid "blocked" msgstr "" #: fotoxx-15.11.cc:786 msgid "edits" msgstr "wijzigingen" #: fotoxx-15.11.cc:2793 msgid "Exceed 50 anchor points" msgstr "50 ankerpunten overschreden" #: fotoxx-15.11.cc:3013 msgid "load curve from a file" msgstr "laad curve vanuit bestand" #: fotoxx-15.11.cc:3079 msgid "curve file is invalid" msgstr "" #: fotoxx-15.11.cc:3093 msgid "save curve to a file" msgstr "bewaar curve in een bestand" #: fotoxx-15.11.cc:3165 msgid "Too many edits, please save image" msgstr "Te veel aanpassingen, graag afbeelding opslaan" #: fotoxx-15.11.cc:3170 msgid "this function cannot be scripted" msgstr "" #: fotoxx-15.11.cc:3187 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Uitsnede niet actief.\n" "Doorgaan?" #: fotoxx-15.11.cc:3499 msgid "file data does not fit dialog" msgstr "bestands gegevens niet passend bij dialoog" #: fotoxx-15.11.cc:3509 msgid "Save settings to a file" msgstr "Bewaren Instellingen bestand" #: fotoxx-15.11.cc:3825 msgid "This action will discard changes to current image" msgstr "Deze actie zal veranderingen weggooien van huidige afbeelding" #: fotoxx-15.11.cc:3826 msgid "prior function still active" msgstr "vorige functie nog actief" #: fotoxx-15.11.cc:3827 fotoxx.h:1128 msgid "Keep" msgstr "Handhaven" #: fotoxx-15.11.cc:3828 msgid "Discard" msgstr "Weggooien" #: fotoxx.h:1080 msgid "Add" msgstr "Toevoegen" #: fotoxx.h:1081 msgid "Add All" msgstr "Alles toevoegen" #: fotoxx.h:1083 msgid "Amount" msgstr "waarde" #: fotoxx.h:1084 msgid "Angle" msgstr "Hoek" #: fotoxx.h:1085 msgid "Apply" msgstr "Toepassen" #: fotoxx.h:1086 msgid "Auto" msgstr "" #: fotoxx.h:1087 msgid "Black" msgstr "Zwart" #: fotoxx.h:1088 msgid "Blend Width" msgstr "Meng breedte" #: fotoxx.h:1090 zfuncs.cc:10098 msgid "bottom" msgstr "onder" #: fotoxx.h:1092 msgid "Browse" msgstr "Zoeken" #: fotoxx.h:1093 msgid "Cancel" msgstr "Annuleren" #: fotoxx.h:1094 msgid "Center" msgstr "centrum" #: fotoxx.h:1095 msgid "Choose" msgstr "Kiezen" #: fotoxx.h:1096 msgid "Clear" msgstr "Herstellen" #: fotoxx.h:1097 msgid "Color" msgstr "Kleur" #: fotoxx.h:1098 msgid "continue" msgstr "" #: fotoxx.h:1100 msgid "Copy" msgstr "Uitsnede kopieren" #: fotoxx.h:1101 msgid "Create" msgstr "Maken" #: fotoxx.h:1102 msgid "Curve File:" msgstr "Curve bestand:" #: fotoxx.h:1103 msgid "Cut" msgstr "Knippen" #: fotoxx.h:1104 msgid "Deband" msgstr "Ontstrepen" #: fotoxx.h:1105 msgid "Delete" msgstr "Uitsnede verwijderen" #: fotoxx.h:1107 msgid "Done" msgstr "Klaar" #: fotoxx.h:1108 msgid "edge" msgstr "rand" #: fotoxx.h:1111 msgid "Erase" msgstr "Verwijderen" #: fotoxx.h:1112 msgid "Fetch" msgstr "Ophalen" #: fotoxx.h:1113 msgid "output file already exists" msgstr "" #: fotoxx.h:1114 #, c-format msgid "%d files selected" msgstr "%d bestanden geselecteerd" #: fotoxx.h:1115 msgid "Find" msgstr "Zoeken" #: fotoxx.h:1116 msgid "Finish" msgstr "Gereed" #: fotoxx.h:1117 msgid "Flatten" msgstr "Afvlakken" #: fotoxx.h:1118 msgid "Font" msgstr "Font" #: fotoxx.h:1119 msgid "Geotags" msgstr "Geotags" #: fotoxx.h:1121 msgid "Grid" msgstr "Raster" #: fotoxx.h:1122 msgid "Height" msgstr "hoogte" #: fotoxx.h:1125 msgid "Images" msgstr "Afbeeldingen" #: fotoxx.h:1126 msgid "Insert" msgstr "Invoegen" #: fotoxx.h:1129 zfuncs.cc:10102 msgid "left" msgstr "links" #: fotoxx.h:1130 msgid "limit" msgstr "limiet" #: fotoxx.h:1131 msgid "Load" msgstr "Laden" #: fotoxx.h:1132 msgid "Make" msgstr "Maken" #: fotoxx.h:1134 msgid "Map" msgstr "Kaart" #: fotoxx.h:1135 msgid "Max" msgstr "Max" #: fotoxx.h:1136 msgid "Negative" msgstr "Negatief" #: fotoxx.h:1137 msgid "New" msgstr "Nieuw" #: fotoxx.h:1138 msgid "Next" msgstr "Volgende" #: fotoxx.h:1139 zfuncs.cc:9190 msgid "No" msgstr "Nee" #: fotoxx.h:1140 msgid "no images" msgstr "geen afbeeldingen" #: fotoxx.h:1142 msgid "no selection" msgstr "geen selectie" #: fotoxx.h:1143 msgid "OK" msgstr "OK" #: fotoxx.h:1145 msgid "Paste" msgstr "Uitsnede plakken" #: fotoxx.h:1146 msgid "Pause" msgstr "Pauze" #: fotoxx.h:1147 msgid "Percent" msgstr "percentage" #: fotoxx.h:1149 msgid "Presets" msgstr "Voorinstelling" #: fotoxx.h:1150 msgid "Prev" msgstr "Vorige" #: fotoxx.h:1151 msgid "Proceed" msgstr "Doorgaan" #: fotoxx.h:1153 msgid "range" msgstr "bereik" #: fotoxx.h:1156 msgid "Reduce" msgstr "verminderen" #: fotoxx.h:1157 msgid "Remove" msgstr "Verwijderen" #: fotoxx.h:1159 msgid "Replace" msgstr "" #: fotoxx.h:1160 msgid "Reserved" msgstr "" #: fotoxx.h:1161 msgid "Reset" msgstr "Reset" #: fotoxx.h:1162 zfuncs.cc:10106 msgid "right" msgstr "rechts" #: fotoxx.h:1163 msgid "Rotate" msgstr "Draaien" #: fotoxx.h:1165 msgid "Unknown file type, save as tiff/jpeg/png to edit" msgstr "Bestandstype onbekend, om te bewerken opslaan als tiff/jpeg/png" #: fotoxx.h:1166 msgid "Search" msgstr "Zoeken" #: fotoxx.h:1167 msgid "Seconds" msgstr "Seconden" #: fotoxx.h:1171 msgid "Size" msgstr "Grootte" #: fotoxx.h:1172 msgid "Start" msgstr "Starten" #: fotoxx.h:1173 msgid "Strength" msgstr "Sterkte" #: fotoxx.h:1174 msgid "Threshold" msgstr "drempel" #: fotoxx.h:1175 #, c-format msgid "exceed %d files" msgstr "overschrijding %d bestanden " #: fotoxx.h:1176 zfuncs.cc:10094 msgid "top" msgstr "boven" #: fotoxx.h:1177 msgid "Transparency" msgstr "Doorzichtigheid" #: fotoxx.h:1179 msgid "Trim" msgstr "Bijsnijden" #: fotoxx.h:1180 msgid "Undo All" msgstr "Alles ongedaan maken" #: fotoxx.h:1181 msgid "Undo Last" msgstr "Laatste ongedaan maken" #: fotoxx.h:1183 msgid "Unfinish" msgstr "Unfinish" #: fotoxx.h:1185 msgid "View" msgstr "Beeld" #: fotoxx.h:1186 msgid "Web" msgstr "Web" #: fotoxx.h:1187 msgid "White" msgstr "Wit" #: fotoxx.h:1188 msgid "Width" msgstr "breedte" #: fotoxx.h:1189 msgid "x-offset" msgstr "x-verschuiving" #: fotoxx.h:1190 msgid "y-offset" msgstr "y-verschuiving" #: fotoxx.h:1191 zfuncs.cc:9190 msgid "Yes" msgstr "Ja" #: zfuncs.cc:1601 #, c-format msgid "" "create directory? \n" " %s" msgstr "" "aanmaken map? \n" " %s" #: zfuncs.cc:4378 msgid "user guide not found" msgstr "gebruikershandleiding niet gevonden" #: zfuncs.cc:5196 #, c-format msgid "cannot open file %s" msgstr "kan bestand niet openen %s" #: zfuncs.cc:5232 msgid "save screen to file" msgstr "schrijf scherm naar bestand" #: zfuncs.cc:9266 zfuncs.cc:9761 zfuncs.cc:10081 msgid "cancel" msgstr "Annuleren" #: zfuncs.cc:9722 msgid "choose file" msgstr "kies bestand" #: zfuncs.cc:9727 msgid "choose files" msgstr "kies bestanden" #: zfuncs.cc:9732 msgid "save" msgstr "opslaan" #: zfuncs.cc:9738 msgid "choose folder" msgstr "kies map" #: zfuncs.cc:9743 msgid "choose folders" msgstr "kies mappen" #: zfuncs.cc:9748 msgid "create folder" msgstr "aanmaken map" #: zfuncs.cc:9755 msgid "hidden" msgstr "verborgen" #: zfuncs.cc:10081 msgid "done" msgstr "klaar" #: zfuncs.cc:10081 zfuncs.cc:10090 msgid "margins" msgstr "marges" #: zfuncs.cc:10111 msgid "image scale" msgstr "afbeeldingsschaal" #: zfuncs.cc:10113 msgid "percent" msgstr "percentage" #: zfuncs.cc:10120 msgid "image" msgstr "afbeelding" #: zfuncs.cc:10124 msgid "width" msgstr "breedte" #: zfuncs.cc:10128 msgid "height" msgstr "hoogte" #~ msgid "Lock aspect ratio" #~ msgstr "lengte:breedte verhouding vasthouden" #~ msgid "Median Brightness" #~ msgstr "Mediane helderheid" #~ msgid "Flatten Outliers 2" #~ msgstr "Uitschieters Afvlakken 2" #~ msgid "Flatten Outliers 1" #~ msgstr "Uitschieters Afvlakken 1" #~ msgid "Kuwahara method" #~ msgstr "Kuwahara methode" #~ msgid "brightness gradient" #~ msgstr "helderheids gradient" #~ msgid "unsharp mask" #~ msgstr "unsharp masking" #~ msgid "" #~ "new name/base/adder unreasonable\n" #~ " e.g. newname ### 100 10" #~ msgstr "" #~ "nieuwe naam/basis/toevoeging niet logisch\n" #~ " bijv. naam ### 100 10" #~ msgid "jpeg quality must be 1-100" #~ msgstr "jpeg kwaliteit moet 1-100 zijn" #~ msgid "(%d images)" #~ msgstr "(%d afbeeldingen)" #~ msgid "(0 images)" #~ msgstr "(0 afbeeldingen)" fotoxx-15.11.1/locales/translate-pt.po0000664000175000017500000033004612616075370016266 0ustar micomico# Portuguese translations for home package. # Copyright (C) 2010 THE home'S COPYRIGHT HOLDER # This file is distributed under the same license as the home package. # mico , 2010. # msgid "" msgstr "" "Project-Id-Version: fotoxx-15.07\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-11-02 21:34+0100\n" "PO-Revision-Date: 2015-09-18 16:43-0300\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.5.4\n" #: f.albums.cc:78 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:80 msgid "Drag album thumbnail to new position." msgstr "Arrastar miniatura de álbum" #: f.albums.cc:109 f.widgets.cc:548 msgid "Manage Albums" msgstr "Gerenciar Álbuns" #: f.albums.cc:120 f.albums.cc:258 msgid "Create or replace an album" msgstr "Criar ou substituir um álbum" #: f.albums.cc:122 msgid "Album to view or edit" msgstr "Álbum para visualizar ou editar" #: f.albums.cc:124 msgid "Select images, add to cache" msgstr "Selecionar imagens, adicionar à reserva" #: f.albums.cc:126 msgid "Clear image cache" msgstr "Limpar reserva de imagens" #: f.albums.cc:128 f.albums.cc:150 msgid "Delete an album" msgstr "Eliminar um álbum" #: f.albums.cc:149 msgid "Choose Album" msgstr "Escolher Álbum" #: f.albums.cc:151 #, c-format msgid "Image cache has %d images" msgstr "" #: f.albums.cc:152 msgid "cache added to empty album" msgstr "reservar adicionadas em um álbum vazio" #: f.albums.cc:207 #, c-format msgid "delete %s ?" msgstr "excluir %s ?" #: f.albums.cc:242 #, c-format msgid "fill from image cache (%d images)" msgstr "preencher com imagens reservadas (%d imagens)" #: f.albums.cc:260 f.albums.cc:297 msgid "Album Name" msgstr "Nome do Álbum" #: f.albums.cc:266 msgid "make an initially empty album" msgstr "criar um álbum inicialmente vazio" #: f.albums.cc:268 msgid "fill from current gallery" msgstr "preencher usando galeria atual" #: f.albums.cc:311 msgid "enter an album name" msgstr "inserir um nome para o álbum" #: f.albums.cc:338 msgid "gallery is empty" msgstr "galeria vazia" #: f.albums.cc:365 msgid "new album created" msgstr "novo álbum criado" #: f.albums.cc:1099 msgid "Press ESC to exit slide show" msgstr "Pressione ESC para sair da apresentação" #: f.albums.cc:1101 msgid "instant" msgstr "imediato" #: f.albums.cc:1102 msgid "fade-in" msgstr "esmaecer" #: f.albums.cc:1103 msgid "roll-right" msgstr "rolar para direita" #: f.albums.cc:1104 msgid "roll-down" msgstr "rolar para baixo" #: f.albums.cc:1105 msgid "venetian" msgstr "veneziano" #: f.albums.cc:1106 msgid "grate" msgstr "grade" #: f.albums.cc:1107 msgid "rectangle" msgstr "retângulo" #: f.albums.cc:1108 msgid "implode" msgstr "implodir" #: f.albums.cc:1109 msgid "explode" msgstr "explodir" #: f.albums.cc:1110 msgid "radar" msgstr "radar" #: f.albums.cc:1111 msgid "Japanese-fan" msgstr "Leque-Japonês" #: f.albums.cc:1112 msgid "jaws" msgstr "maxilas" #: f.albums.cc:1113 msgid "ellipse" msgstr "elipse" #: f.albums.cc:1114 msgid "raindrops" msgstr "gotas de chuva" #: f.albums.cc:1115 msgid "doubledoor" msgstr "doubledoor" #: f.albums.cc:1116 msgid "rotate" msgstr "rotacionar" #: f.albums.cc:1117 msgid "fallover" msgstr "tombar" #: f.albums.cc:1118 msgid "sphereoid" msgstr "" #: f.albums.cc:1139 f.widgets.cc:549 msgid "Slide Show" msgstr "Apresentação" #: f.albums.cc:1150 msgid "Clip Limit" msgstr "Limite de Corte" #: f.albums.cc:1154 msgid "Music File" msgstr "Arquivo de Música" #: f.albums.cc:1159 msgid "Full Screen" msgstr "Tela cheia" #: f.albums.cc:1160 msgid "Auto-replay" msgstr "Auto-repetição" #: f.albums.cc:1166 msgid "Customize:" msgstr "Customizar:" #: f.albums.cc:1167 msgid "transitions" msgstr "transições" #: f.albums.cc:1168 msgid "image files" msgstr "arquivos de imagem" #: f.albums.cc:1173 f.albums.cc:1289 #, c-format msgid "%d images" msgstr "%d imagens" #: f.albums.cc:1195 f.albums.cc:1259 f.albums.cc:1349 f.albums.cc:1462 #: f.albums.cc:1655 f.albums.cc:1739 f.albums.cc:1756 f.albums.cc:1977 msgid "invalid album" msgstr "álbum inválido" #: f.albums.cc:1273 msgid "open album" msgstr "abrir álbum" #: f.albums.cc:1305 msgid "Select music file" msgstr "Selecionar arquivo de música" #: f.albums.cc:1329 msgid "select random (if 5+ enabled)" msgstr "selecionar aleatoriamente (se 5+ habilitadas)" #: f.albums.cc:1353 msgid "Transition Preferences" msgstr "Preferências de Transição" #: f.albums.cc:1362 msgid "transition" msgstr "transição" #: f.albums.cc:1363 msgid "enabled" msgstr "habilitado" #: f.albums.cc:1364 msgid "slowdown" msgstr "desacelerar" #: f.albums.cc:1365 msgid "preference" msgstr "preferência" #: f.albums.cc:1468 msgid "Image Preferences" msgstr "Preferências de Imagem" #: f.albums.cc:1472 f.file.cc:1159 f.file.cc:1400 f.file.cc:1608 f.meta.cc:437 msgid "Image File:" msgstr "Arquivo de Imagem:" #: f.albums.cc:1476 msgid "Play tone when image shows" msgstr "Tocar áudio quando imagem aparecer" #: f.albums.cc:1479 msgid "Show image caption" msgstr "Mostrar legenda de imagem" #: f.albums.cc:1484 msgid "Show image comments" msgstr "Mostrar comentários de imagem" #: f.albums.cc:1489 msgid "Wait before zoom" msgstr "Atraso antes do Zoom" #: f.albums.cc:1494 msgid "Zoom type:" msgstr "Tipo de Zoom:" #: f.albums.cc:1495 msgid "none" msgstr "nenhum" #: f.albums.cc:1496 msgid "zoom-in" msgstr "aproximar" #: f.albums.cc:1497 msgid "zoom-out" msgstr "afastar" #: f.albums.cc:1500 msgid "Zoom size (x)" msgstr "Tamanho do Zoom (x)" #: f.albums.cc:1503 msgid "Steps" msgstr "Passos" #: f.albums.cc:1507 msgid "Zoom image location:" msgstr "Localização de Zoom na imagem:" #: f.albums.cc:1515 msgid "Wait after zoom" msgstr "Atraso depois do Zoom" #: f.albums.cc:1520 msgid "Transition to next image" msgstr "Transição para a próxima imagem" #: f.albums.cc:1523 f.albums.cc:1625 msgid "next" msgstr "próximo" #: f.albums.cc:1962 f.albums.cc:2120 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "error no formato de arquivo: \n" " %s" #: f.area.cc:79 msgid "Select Area for Edits" msgstr "Selecionar área para edições" #: f.area.cc:80 f.edit.cc:5927 msgid "Press F1 for help" msgstr "Pressione F1 para ajuda" #: f.area.cc:89 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:119 msgid "select rectangle" msgstr "seleção retangular" #: f.area.cc:120 msgid "select ellipse" msgstr "seleção elíptica" #: f.area.cc:123 msgid "freehand draw" msgstr "desenho a mão livre" #: f.area.cc:124 msgid "follow edge" msgstr "seguir borda" #: f.area.cc:125 msgid "draw/replace" msgstr "desenho/substituir" #: f.area.cc:128 msgid "select area within mouse" msgstr "selecionar área delimitando com o mouse" #: f.area.cc:131 msgid "select one matching color within mouse" msgstr "selecionar cor correspondente com o mouse" #: f.area.cc:135 msgid "select all matching colors within mouse" msgstr "selecionar todas as cores correspondentes com o mouse" #: f.area.cc:139 f.edit.cc:5972 msgid "mouse radius" msgstr "raio do mouse" #: f.area.cc:142 msgid "match level %" msgstr "nível de correspondência %" #: f.area.cc:147 msgid "search range" msgstr "intervalo de procura" #: f.area.cc:149 msgid "firewall" msgstr "corta-fogo" #: f.area.cc:152 msgid "Area Edge Blend Width" msgstr "Largura da Área de Borda da Mistura " #: f.area.cc:155 msgid "Edge Creep" msgstr "Deslizamento de Borda" #: f.area.cc:160 msgid "Line Color:" msgstr "Cor de Linha:" #: f.area.cc:180 f.area.cc:181 msgid "area edits fade away within edge distance" msgstr "edições na área esmaecem dentre as distâncias de borda" #: f.area.cc:360 f.area.cc:543 #, c-format msgid "exceed %d edits" msgstr "excedeu %d edições" #: f.area.cc:1364 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 inclusa \n" "(possíveis fendas no contorno serão encontradas). \n" "Pressione F1 para ajuda." #: f.area.cc:1392 msgid "finish area" msgstr "finalize a área" #: f.area.cc:1400 msgid "extend to corner:" msgstr "estender até canto:" #: f.area.cc:1441 msgid "searching" msgstr "procurando" #: f.area.cc:1472 msgid "outline has a gap" msgstr "contorno tem uma fenda" #: f.area.cc:1476 msgid "success" msgstr "sucesso" #: f.area.cc:1613 f.area.cc:1640 #, c-format msgid "found %d pixels" msgstr "encontrados %d pixels" #: f.area.cc:1935 f.area.cc:1952 f.area.cc:1972 f.area.cc:2287 f.area.cc:2363 #: f.area.cc:2520 msgid "the area is not finished" msgstr "a área não está finalizada" #: f.area.cc:2061 msgid "Edge calculation in progress" msgstr "Cálculo de borda em andamento" #: f.area.cc:2070 msgid "Area Edge Calc" msgstr "Calc. área de borda" #: f.area.cc:2471 msgid "load area from a file" msgstr "carregar área a partir de arquivo" #: f.area.cc:2554 msgid "save area as a PNG file" msgstr "salvar área como um arquivo PNG" #: f.area.cc:2582 msgid "position with mouse click/drag" msgstr "posicione o mouse clique/arraste" #: f.area.cc:2638 msgid "Paste Image" msgstr "Colar Imagem" #: f.area.cc:2643 f.batch.cc:1169 f.batch.cc:1660 msgid "resize" msgstr "redimensionar" #: f.batch.cc:107 f.widgets.cc:531 msgid "Batch Convert" msgstr "Conversão em Lote" #: f.batch.cc:114 f.file.cc:942 msgid "New Name" msgstr "novo nome" #: f.batch.cc:118 msgid "Sequence Numbers" msgstr "" #: f.batch.cc:120 msgid "base" msgstr "base" #: f.batch.cc:122 msgid "adder" msgstr "somador" #: f.batch.cc:127 msgid "New Location" msgstr "Nova Localidade" #: f.batch.cc:132 msgid "New File Type" msgstr "Novo Tipo de Arquivo" #: f.batch.cc:136 f.batch.cc:144 f.repair.cc:3861 msgid "no change" msgstr "sem mudanças" #: f.batch.cc:139 msgid "max. Width" msgstr "Largura max." #: f.batch.cc:147 f.batch.cc:565 msgid "Delete Originals" msgstr "Excluir Originais" #: f.batch.cc:148 f.batch.cc:562 msgid "Copy Metadata" msgstr "Copiar Metadados" #: f.batch.cc:149 f.batch.cc:563 f.edit.cc:1413 f.widgets.cc:438 #: f.widgets.cc:684 msgid "Upright" msgstr "Orientar" #: f.batch.cc:152 f.batch.cc:564 f.batch.cc:1178 f.batch.cc:1669 #: f.repair.cc:118 f.widgets.cc:454 msgid "Sharpen" msgstr "Aguçar" #: f.batch.cc:154 f.batch.cc:1180 f.batch.cc:1671 msgid "amount" msgstr "quantia" #: f.batch.cc:157 f.batch.cc:1183 f.batch.cc:1674 msgid "threshold" msgstr "limiar" #: f.batch.cc:280 msgid "file type not supported" msgstr "tipo de arquivo não suportado" #: f.batch.cc:344 msgid "cannot create new file" msgstr "impossível criar novo arquivo" #: f.batch.cc:391 msgid "updating albums ..." msgstr "atualizando álbuns ..." #: f.batch.cc:464 f.batch.cc:1456 f.batch.cc:1862 f.file.cc:1208 msgid "Select directory" msgstr "Selecionar pasta" #: f.batch.cc:513 msgid "" "new name/base/adder unreasonable\n" " e.g. newname ### 100 10\n" " or ... [oldname] ..." msgstr "" #: f.batch.cc:537 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "tamanho max. %d x %d não é razoável" #: f.batch.cc:556 #, c-format msgid "Convert %d image files" msgstr "Converter %d arquivos de imagem" #: f.batch.cc:557 msgid "Rename to" msgstr "Renomear para" #: f.batch.cc:558 msgid "Convert to" msgstr "Converter para" #: f.batch.cc:559 msgid "Resize within" msgstr "Redimensionar dentro de" #: f.batch.cc:560 msgid "Output to" msgstr "Saída para" #: f.batch.cc:566 msgid "PROCEED?" msgstr "PROCEDER?" #: f.batch.cc:720 f.widgets.cc:532 msgid "Batch Upright" msgstr "Orientação em Lote" #: f.batch.cc:726 msgid "Survey all files" msgstr "Examinar todos arquivos" #: f.batch.cc:780 msgid "file cannot be read" msgstr "arquivo não pode ser lido" #: f.batch.cc:898 msgid "cannot select both options" msgstr "não é possível selecionar ambas as opções" #: f.batch.cc:942 f.widgets.cc:533 msgid "Batch Delete/Trash" msgstr "Excluir/Descartar em Lote" #: f.batch.cc:947 msgid "delete" msgstr "excluir" #: f.batch.cc:950 msgid "trash" msgstr "lixo" #: f.batch.cc:1112 msgid "Batch Convert RAW Files (DCraw)" msgstr "Conversão em Lote de Arquivos RAW (DCraw)" #: f.batch.cc:1121 msgid "DCraw not installed" msgstr "DCraw não instalado" #: f.batch.cc:1156 f.batch.cc:1647 msgid "output location" msgstr "localização de saída" #: f.batch.cc:1161 f.batch.cc:1652 msgid "output file type" msgstr "tipo de arquivo de saída" #: f.batch.cc:1192 msgid "white balance" msgstr "balanço de branco" #: f.batch.cc:1193 msgid "interpolation" msgstr "interpolação" #: f.batch.cc:1194 msgid "color space" msgstr "espaço de cor" #: f.batch.cc:1195 msgid "gamma curve" msgstr "curva de gama" #: f.batch.cc:1198 msgid "camera" msgstr "camera" #: f.batch.cc:1199 msgid "fixed" msgstr "fixado" #: f.batch.cc:1200 msgid "calculated" msgstr "calculado" #: f.batch.cc:1217 msgid "default" msgstr "padrão" #: f.batch.cc:1223 msgid "defaults" msgstr "padrões" #: f.batch.cc:1610 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Conversão em Lote de Arquivos RAW (RAW Therapee)" #: f.batch.cc:1619 f.file.cc:428 msgid "Raw Therapee not installed" msgstr "Raw Therapee não instalado" #: f.batch.cc:1959 msgid "start" msgstr "iniciar" #: f.batch.cc:1960 msgid "begin making a script file" msgstr "iniciar a criação de um arquivo de script" #: f.batch.cc:1962 msgid "close" msgstr "fechar" #: f.batch.cc:1963 msgid "finish making a script file" msgstr "terminar a criação do arquivo de script" #: f.batch.cc:1965 msgid "run" msgstr "executar" #: f.batch.cc:1966 msgid "execute a script file" msgstr "executar o arquivo de script" #: f.batch.cc:2020 msgid "script already started" msgstr "script já foi iniciado" #: f.batch.cc:2024 msgid "open new script file" msgstr "abrir novo arquivo de script" #: f.batch.cc:2056 msgid "script file error" msgstr "erro no arquivo de script" #: f.batch.cc:2061 #, c-format msgid "%s added to script" msgstr "%s adicionado ao script" #: f.batch.cc:2074 msgid "no script file was started" msgstr "nenhum arquivo de script foi iniciado" #: f.batch.cc:2082 msgid "script file closed" msgstr "arquivo de script finalizado" #: f.batch.cc:2101 msgid "script file is not closed" msgstr "arquivo de script não finalizado" #: f.batch.cc:2105 msgid "select script file to run" msgstr "selecione o arquivo de script a executar" #: f.batch.cc:2107 msgid "open script file" msgstr "abrir arquivo de script" #: f.batch.cc:2114 msgid "unknown script file" msgstr "arquivo de script desconhecido" #: f.batch.cc:2121 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" "arquivo de script: %s \n" " %s" #: f.batch.cc:2132 msgid "select image files to be processed" msgstr "selecione arquivos de imagem para serem processados" #: f.batch.cc:2144 f.batch.cc:2193 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "erro ao abrir: %s \n" " %s" #: f.batch.cc:2165 #, c-format msgid "unknown edit function: %s" msgstr "função de edição desconhecida: %s" #: f.batch.cc:2174 #, c-format msgid "load widgets failed: %s" msgstr "carregamento de dispositivo falhou: %s" #: f.batch.cc:2203 #, c-format msgid "script file format error: %s" msgstr "erro no formato do arquivo de script: %s" #: f.batch.cc:2229 msgid "Brasero not installed" msgstr "Brasero não instalado" #: f.batch.cc:2318 f.widgets.cc:538 msgid "Find Duplicate Images" msgstr "Encontrar imagens Duplicadas" #: f.batch.cc:2320 f.tools.cc:929 msgid "Thumbnail size" msgstr "Tamanho de miniatura" #: f.batch.cc:2323 msgid "pixel difference" msgstr "diferença de pixel" #: f.batch.cc:2326 msgid "pixel count" msgstr "contagem de pixel" #: f.batch.cc:2329 msgid "Images:" msgstr "Imagens:" #: f.batch.cc:2330 msgid "searching ..." msgstr "procurando ..." #: f.batch.cc:2332 msgid "Duplicates:" msgstr "Duplicidades:" #: f.batch.cc:2350 #, c-format msgid "only %d image thumbnails found" msgstr "apenas %d miniaturas de imagem encontrados" #: f.bend.cc:86 f.widgets.cc:475 msgid "Unbend" msgstr "Desempenar" #: f.bend.cc:94 f.edit.cc:4267 f.effects.cc:4725 msgid "vertical" msgstr "vertical" #: f.bend.cc:95 f.edit.cc:4266 f.effects.cc:4721 msgid "horizontal" msgstr "horizontal" #: f.bend.cc:96 msgid "linear" msgstr "linear" #: f.bend.cc:99 msgid "curved" msgstr "curvo" #: f.bend.cc:367 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.bend.cc:384 msgid "Perspective Correction" msgstr "Correção de Perspectiva" #: f.bend.cc:590 msgid "must have 4 corners" msgstr "precisar ter 4 cantos" #: f.bend.cc:712 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.bend.cc:725 f.widgets.cc:477 msgid "Warp area" msgstr "Distorcer área" #: f.bend.cc:730 msgid "start warp" msgstr "iniciar distorção" #: f.bend.cc:788 msgid "no active Select Area" msgstr "nenhuma Área Selecionada ativa" #: f.bend.cc:1129 f.bend.cc:1443 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.bend.cc:1147 f.widgets.cc:478 msgid "Warp curved" msgstr "Distorção curva" #: f.bend.cc:1156 f.mashup.cc:1836 msgid "warp span" msgstr "distorção em alcance" #: f.bend.cc:1461 f.widgets.cc:479 msgid "Warp linear" msgstr "Distorção linear" #: f.bend.cc:1776 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.bend.cc:1792 f.widgets.cc:480 msgid "Warp affine" msgstr "Distorção afim" #: f.bend.cc:2138 msgid "Flatten Book Page Photo" msgstr "Achatar foto de Página de Book" #: f.bend.cc:2139 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.bend.cc:2142 msgid "Stretch curved-down surfaces:" msgstr "Estique superfícies curvadas para baixo:" #: f.bend.cc:2197 msgid "top:" msgstr "topo:" #: f.bend.cc:2200 msgid "bottom:" msgstr "rodapé:" #: f.combine.cc:2108 f.combine.cc:2740 f.combine.cc:3396 f.combine.cc:3928 msgid "Select 2 to 9 files" msgstr "Selecionar 2 a 9 arquivos" #: f.combine.cc:2129 f.combine.cc:2761 f.combine.cc:3417 f.combine.cc:3949 msgid "Images are not all the same size" msgstr "Imagens não são todas do mesmo tamanho" #: f.combine.cc:2473 msgid "Adjust Image Contributions" msgstr "Ajustar contribuição de imagem" #: f.combine.cc:2477 msgid "dark pixels" msgstr "pixels escuros" #: f.combine.cc:2479 msgid "light pixels" msgstr "pixels claros" #: f.combine.cc:2481 msgid "file:" msgstr "arquivo:" #: f.combine.cc:2997 msgid "Paint and Warp Image" msgstr "Pintar e distorcer imagem" #: f.combine.cc:3001 f.combine.cc:3646 f.combine.cc:6237 f.effects.cc:3912 #: f.widgets.cc:567 f.widgets.cc:590 f.widgets.cc:612 msgid "Image" msgstr "Imagem" #: f.combine.cc:3005 msgid "paint" msgstr "pintar" #: f.combine.cc:3006 msgid "warp" msgstr "distorção" #: f.combine.cc:3644 msgid "Select and Paint Image" msgstr "Selecionar e pintar imagem" #: f.combine.cc:4120 msgid "Adjust Pixel Composition" msgstr "Ajustar Composição do Pixel" #: f.combine.cc:4122 msgid "use average" msgstr "usar média" #: f.combine.cc:4123 msgid "use median" msgstr "usar mediana" #: f.combine.cc:4125 msgid "omit low pixel" msgstr "omitir pixel baixos" #: f.combine.cc:4126 msgid "omit high pixel" msgstr "omitir pixels altos" #: f.combine.cc:4388 f.combine.cc:5494 msgid "Select 2 to 4 files" msgstr "Selecionar 2 a 4 arquivos" #: f.combine.cc:4463 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:4465 f.combine.cc:5571 msgid "no curve (scanned image)" msgstr "não vetorial (imagem digitalizada)" #: f.combine.cc:4466 f.combine.cc:5572 msgid "Search for lens mm" msgstr "Procurar por mm de lente" #: f.combine.cc:4467 f.combine.cc:5573 msgid "Save lens mm → image EXIF" msgstr "Salvar mm de lente → EXIF imagem" #: f.combine.cc:4528 f.combine.cc:5634 msgid "Pre-align Images" msgstr "Pré-alinhamento de imagens" #: f.combine.cc:4532 f.combine.cc:5638 msgid "lens mm" msgstr "mm de lente" #: f.combine.cc:4537 f.combine.cc:5643 msgid "no auto warp" msgstr "não auto distorcer" #: f.combine.cc:4539 f.combine.cc:5645 f.widgets.cc:445 f.widgets.cc:683 msgid "Resize" msgstr "Redimensionar" #: f.combine.cc:4540 f.combine.cc:5646 msgid "resize window" msgstr "redimensionar janela" #: f.combine.cc:4548 f.combine.cc:5654 msgid "do not warp images during auto-alignment" msgstr "não distorcer imagem durante auto-alinhamento" #: f.combine.cc:4593 f.combine.cc:5699 msgid "use two images only" msgstr "use duas imagens apenas" #: f.combine.cc:4621 f.combine.cc:4825 f.combine.cc:5011 f.combine.cc:5725 #: f.combine.cc:5929 f.combine.cc:6115 msgid "Too little overlap, cannot align" msgstr "Baixa sobreposição, não é possível alinhar" #: f.combine.cc:5089 f.combine.cc:6193 msgid "Match Brightness and Color" msgstr "Parear Brilho e Cor" #: f.combine.cc:5134 msgid "Select image" msgstr "Selecionar imagem" #: f.combine.cc:5149 f.combine.cc:6252 msgid "auto color" msgstr "cor automática" #: f.combine.cc:5150 f.combine.cc:6253 msgid "file color" msgstr "arquivo de cor" #: f.combine.cc:5156 f.combine.cc:6259 msgid "mouse warp" msgstr "distorcer com mouse" #: f.combine.cc:5159 msgid "curved image" msgstr "imagem curvada" #: f.combine.cc:5569 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:6469 msgid "pano tools (hugin) not installed" msgstr "pano tools (hugin) não instalado" #: f.combine.cc:6478 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:173 f.widgets.cc:437 f.widgets.cc:682 msgid "Trim/Rotate" msgstr "Recortar/Rodar" #: f.edit.cc:185 f.edit.cc:586 msgid "ratio" msgstr "taxa" #: f.edit.cc:189 msgid "trim size:" msgstr "tamanho do recorte:" #: f.edit.cc:194 msgid "Lock Ratio" msgstr "Travar taxa" #: f.edit.cc:199 msgid "Customize" msgstr "Customizar" #: f.edit.cc:207 msgid "Rotate: degrees" msgstr "Rotação: graus" #: f.edit.cc:209 msgid "auto-trim" msgstr "auto-recorte" #: f.edit.cc:582 msgid "Trim Buttons" msgstr "Aparar rodapés" #: f.edit.cc:585 msgid "label" msgstr "etiqueta" #: f.edit.cc:1411 msgid "Upright Image" msgstr "Orientar Imagem" #: f.edit.cc:1477 msgid "rotation unknown" msgstr "rotação desconhecida" #: f.edit.cc:2029 f.widgets.cc:441 f.widgets.cc:686 msgid "Retouch Combo" msgstr "Kit Retoque" #: f.edit.cc:2050 msgid "Amplifier" msgstr "Amplificador" #: f.edit.cc:2051 fotoxx.h:1091 msgid "Brightness" msgstr "Briho" #: f.edit.cc:2052 f.effects.cc:3364 fotoxx.h:1099 msgid "Contrast" msgstr "Contraste" #: f.edit.cc:2053 msgid "Low Color" msgstr "Cor baixa" #: f.edit.cc:2054 msgid "Warmer" msgstr "Mais quente" #: f.edit.cc:2055 f.effects.cc:1011 msgid "Dark Areas" msgstr "Áreas Escuras" #: f.edit.cc:2064 msgid "Max." msgstr "Máx." #: f.edit.cc:2065 f.edit.cc:2066 f.edit.cc:2067 msgid "High" msgstr "Alto" #: f.edit.cc:2068 msgid "Cooler" msgstr "Mais frio" #: f.edit.cc:2069 msgid "Bright" msgstr "Brilho" #: f.edit.cc:2072 f.tools.cc:1631 msgid "Brightness Distribution" msgstr "Distribuição de Brilho" #: f.edit.cc:2075 msgid "Click for white balance or black level" msgstr "Clique para balancear o nível de branco ou preto" #: f.edit.cc:2078 msgid "Settings File" msgstr "Arquivos de Configuração" #: f.edit.cc:2082 msgid "recall previous settings used" msgstr "recolocar configurações prévias utilizadas" #: f.edit.cc:2726 msgid "Adjust Brightness Distribution" msgstr "Ajustar Distribuição de Brilho" #: f.edit.cc:2765 msgid "Low Cutoff" msgstr "Corte Baixo" #: f.edit.cc:2766 msgid "High Cutoff" msgstr "Corte Elevado" #: f.edit.cc:2767 msgid "Low Flatten" msgstr "Achatamento Baixo" #: f.edit.cc:2768 msgid "Mid Flatten" msgstr "Achatamento Médio" #: f.edit.cc:2769 msgid "High Flatten" msgstr "Achatamento Elevado" #: f.edit.cc:2770 msgid "Low Stretch" msgstr "Estiramento Baixo" #: f.edit.cc:2771 msgid "Mid Stretch" msgstr "Estiramento Médio" #: f.edit.cc:2772 msgid "High Stretch" msgstr "Estiramento Elevado" #: f.edit.cc:3102 msgid "Zonal Flatten Brightness" msgstr "Brilho de Achatamento Zonal" #: f.edit.cc:3135 msgid "Zones" msgstr "Zonas" #: f.edit.cc:3142 msgid "Deband Dark" msgstr "Debandar Escuridão" #: f.edit.cc:3145 msgid "Deband Bright" msgstr "Debandar Claridade" #: f.edit.cc:3594 f.widgets.cc:444 f.widgets.cc:689 msgid "Tone Mapping" msgstr "Mapeamento Tonal" #: f.edit.cc:3625 msgid "low" msgstr "baixo" #: f.edit.cc:3627 msgid "high" msgstr "alto" #: f.edit.cc:3630 msgid "Amplify" msgstr "Ampliar" #: f.edit.cc:4034 msgid "Resize Image" msgstr "Redimensionar" #: f.edit.cc:4059 msgid "W/H Ratio:" msgstr "Relação L/A:" #: f.edit.cc:4061 msgid "Lock" msgstr "Travar" #: f.edit.cc:4063 msgid "use previous settings" msgstr "usar configurações prévias" #: f.edit.cc:4262 f.widgets.cc:446 msgid "Flip" msgstr "Virar" #: f.edit.cc:4368 msgid "+Version" msgstr "+Versão" #: f.edit.cc:4391 msgid "Write Text on Image" msgstr "Escrever Texto na Imagem" #: f.edit.cc:4392 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:4441 f.mashup.cc:2437 msgid "Text" msgstr "Texto" #: f.edit.cc:4446 msgid "Use metadata key" msgstr "Use chave de metadado" #: f.edit.cc:4451 msgid "Use text file" msgstr "Usar arquivo de texto" #: f.edit.cc:4470 f.mashup.cc:2455 msgid "text" msgstr "texto" #: f.edit.cc:4471 f.edit.cc:5309 f.mashup.cc:2456 f.mashup.cc:2798 msgid "backing" msgstr "fundo" #: f.edit.cc:4472 f.edit.cc:5310 f.mashup.cc:2457 f.mashup.cc:2799 msgid "outline" msgstr "contorno" #: f.edit.cc:4473 f.edit.cc:5311 f.mashup.cc:2458 f.mashup.cc:2800 msgid "shadow" msgstr "sombra" #: f.edit.cc:4499 msgid "save to current file" msgstr "salvar em arquivo atual" #: f.edit.cc:4500 f.file.cc:1909 msgid "save as new file version" msgstr "salvar como nova versão de arquivo" #: f.edit.cc:4501 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:4662 f.mashup.cc:2557 f.tools.cc:1068 msgid "select font" msgstr "selecionar fonte" #: f.edit.cc:4926 msgid "text file is defective" msgstr "arquivo de texto está defeituoso" #: f.edit.cc:5249 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:5280 msgid "Write Line or Arrow on Image" msgstr "Gravar Linha ou Seta na Imagem" #: f.edit.cc:5288 f.mashup.cc:2777 msgid "Line length" msgstr "Comprimento de linha" #: f.edit.cc:5295 f.mashup.cc:2784 msgid "Arrow head" msgstr "Ponta de seta" #: f.edit.cc:5308 f.mashup.cc:2797 msgid "line" msgstr "linha" #: f.edit.cc:5337 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:5926 f.widgets.cc:449 msgid "Paint Edits" msgstr "Primir Edições" #: f.edit.cc:5933 f.edit.cc:6162 fotoxx-15.11.cc:3179 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "Área selecionada não pode ser capturada.\n" "Continuar?" #: f.edit.cc:5941 f.edit.cc:6170 f.tools.cc:2157 msgid "Edit function must be active" msgstr "Função Editar tem de estar ativada" #: f.edit.cc:5946 msgid "Cannot use Paint Edits" msgstr "Impossível usar Edições Aplicadas" #: f.edit.cc:5975 msgid "power: center" msgstr "alimentação: centro" #: f.edit.cc:5980 msgid "reset area" msgstr "redefinir área" #: f.edit.cc:6155 f.widgets.cc:450 msgid "Leverage Edits" msgstr "Influenciar Edições" #: f.edit.cc:6156 msgid "Edit Function Amplifier" msgstr "Editar função Amplificador" #: f.edit.cc:6175 msgid "Cannot use Leverage Edits" msgstr "Impossível usar Edições Influenciadas" #: f.edit.cc:6208 msgid "minimum" msgstr "mínimo" #: f.edit.cc:6210 msgid "maximum" msgstr "máximo" #: f.edit.cc:6510 msgid "Edit Plugins" msgstr "Editar Extensões" #: f.edit.cc:6511 msgid "Edit plugins menu" msgstr "Editar menu de plugins" #: f.edit.cc:6519 msgid "Run as Fotoxx edit function" msgstr "Executar como função de edição do Fotoxx" #: f.edit.cc:6757 msgid "Plugin working ..." msgstr "Plugin funcionando ..." #: f.edit.cc:6766 msgid "plugin failed" msgstr "extensão falhou" #: f.effects.cc:64 msgid "Set color depth to 1-16 bits" msgstr "Configurar resolução de cor para 1-16 bits" #: f.effects.cc:76 msgid "Set Color Depth" msgstr "Configurar resolução de cor" #: f.effects.cc:220 msgid "Convert to Sketch" msgstr "Converter em Esboço" #: f.effects.cc:296 msgid "Clip Level" msgstr "Grampear Nível" #: f.effects.cc:300 msgid "Algorithm" msgstr "Algoritmo" #: f.effects.cc:305 msgid "Foreground" msgstr "Primeiro Plano" #: f.effects.cc:308 msgid "Background" msgstr "Plano de Fundo" #: f.effects.cc:714 f.widgets.cc:486 msgid "Line Drawing" msgstr "Desenho de linha" #: f.effects.cc:727 msgid "black/white" msgstr "preto/branco" #: f.effects.cc:1005 f.widgets.cc:487 msgid "Color Drawing" msgstr "Desenho de Cores" #: f.effects.cc:1012 msgid "Bright Areas" msgstr "Áreas claras" #: f.effects.cc:1266 f.widgets.cc:488 msgid "Graduated Blur" msgstr "Desfoque Graduado" #: f.effects.cc:1270 msgid "Contrast Limit" msgstr "Limite de Contraste" #: f.effects.cc:1273 f.repair.cc:775 msgid "Blur Radius" msgstr "Raio de Desfoque" #: f.effects.cc:1487 msgid "Simulate Embossing" msgstr "Simular Gravação em relevo" #: f.effects.cc:1504 msgid "depth" msgstr "profundidade" #: f.effects.cc:1706 msgid "Simulate Tiles" msgstr "Simular Ladrilho" #: f.effects.cc:1710 msgid "tile size" msgstr "tamanho do ladrilho" #: f.effects.cc:1713 msgid "tile gap" msgstr "espaçamento dos ladrilhos" #: f.effects.cc:1716 msgid "3D depth" msgstr "profundidade 3D" #: f.effects.cc:1940 msgid "Convert Image to Dots" msgstr "Converter imagem para pontos" #: f.effects.cc:1944 msgid "dot size" msgstr "tamanho do ponto" #: f.effects.cc:2169 msgid "Simulate Painting" msgstr "Simular Pintura" #: f.effects.cc:2173 msgid "color depth" msgstr "resolução de cor" #: f.effects.cc:2177 msgid "patch area goal" msgstr "remendar área objetivo" #: f.effects.cc:2181 msgid "req. color match" msgstr "correspondência de cores requeridas" #: f.effects.cc:2185 msgid "borders" msgstr "bordas" #: f.effects.cc:2758 f.widgets.cc:493 msgid "Vignette" msgstr "Vinheta" #: f.effects.cc:3107 msgid "Add Texture" msgstr "Adicionar Textura" #: f.effects.cc:3324 msgid "Background Pattern" msgstr "Padrão de Plano de Fundo" #: f.effects.cc:3328 msgid "Pattern File:" msgstr "Arquivo de Padrão:" #: f.effects.cc:3333 msgid "Zoom" msgstr "Zoom" #: f.effects.cc:3336 msgid "Geometry" msgstr "Geometria" #: f.effects.cc:3337 msgid "Calculate" msgstr "Calcular" #: f.effects.cc:3347 f.widgets.cc:495 msgid "Pattern" msgstr "Padrão" #: f.effects.cc:3354 msgid "Overlap" msgstr "Sobreposição" #: f.effects.cc:3361 msgid "Opacity" msgstr "Opacidade" #: f.effects.cc:3429 msgid "choose pattern tile" msgstr "escolha padrão do ladrilho" #: f.effects.cc:3816 msgid "Create Mosaic" msgstr "Criar Mosaico" #: f.effects.cc:3902 msgid "Tile" msgstr "Ladrilho" #: f.effects.cc:3910 f.widgets.cc:490 msgid "Tiles" msgstr "Ladrilhos" #: f.effects.cc:3916 msgid "Tile blending" msgstr "Mistura de Ladrilhos" #: f.effects.cc:3996 #, c-format msgid "exceeded max. tiles: %d" msgstr "excedeu máx. de ladrilhos: %d" #: f.effects.cc:4011 #, c-format msgid "only %d tile images found" msgstr "apenas %d imagens de ladrilho encontradas" #: f.effects.cc:4404 f.widgets.cc:497 msgid "Custom Kernel" msgstr "Customizar Núcleo" #: f.effects.cc:4408 msgid "Kernel size" msgstr "Tamanho de Núcleo" #: f.effects.cc:4411 msgid "Divisor" msgstr "Divisor" #: f.effects.cc:4414 msgid "Data file" msgstr "Arquivo de Dados" #: f.effects.cc:4450 fotoxx-15.11.cc:3440 msgid "Load settings from file" msgstr "Carregar configurações de um arquivo" #: f.effects.cc:4711 f.widgets.cc:498 msgid "Make Waves" msgstr "Criar Ondas" #: f.effects.cc:4718 msgid "wavelength" msgstr "comprimento de onda" #: f.effects.cc:4719 msgid "amplitude" msgstr "amplitude" #: f.effects.cc:4720 msgid "variance" msgstr "variância" #: f.effects.cc:4731 msgid "perspective" msgstr "perspectiva" #: f.effects.cc:4905 msgid "Pull image using the mouse." msgstr "Puxe a imagem utilizando o mouse." #: f.effects.cc:4926 f.widgets.cc:499 msgid "Directed Blur" msgstr "Desfoque Direcionado" #: f.effects.cc:4930 msgid "blur span" msgstr "extensão de desfoque" #: f.effects.cc:4933 msgid "intensity" msgstr "intensidade" #: f.effects.cc:5099 f.widgets.cc:500 msgid "Spherical Projection" msgstr "Projeção Esférica" #: f.effects.cc:5143 msgid "Magnify" msgstr "Ampliar" #: f.file.cc:186 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:203 f.widgets.cc:572 msgid "File" msgstr "Arquivo" #: f.file.cc:357 msgid "UFraw not installed" msgstr "UFraw não instalado" #: f.file.cc:373 f.widgets.cc:691 msgid "Open RAW file (ufraw)" msgstr "Abrir arquivo RAW (ufraw)" #: f.file.cc:378 f.file.cc:449 msgid "RAW type not registered in User Settings" msgstr "Tipo RAW não reconhecido nas Configurações de Usuário" #: f.file.cc:444 f.widgets.cc:692 msgid "Open RAW file (Raw Therapee)" msgstr "Abrir arquivo RAW (Raw Therapee)" #: f.file.cc:541 f.widgets.cc:395 msgid "Open Image File" msgstr "Abrir arquivo" #: f.file.cc:560 msgid "unknown file type" msgstr "tipo de arquivo desconhecido" #: f.file.cc:753 msgid "Create Blank Image" msgstr "Criar imagem em branco" #: f.file.cc:755 msgid "file name" msgstr "nome do arquvo" #: f.file.cc:788 msgid "supply a file name" msgstr "forneça um nome de arquivo" #: f.file.cc:934 f.widgets.cc:400 msgid "Rename Image File" msgstr "Renomear arquivo" #: f.file.cc:941 msgid "Old Name" msgstr "nome antigo" #: f.file.cc:951 msgid "previous name" msgstr "nome anterior" #: f.file.cc:952 msgid "add 1" msgstr "adicionar 1" #: f.file.cc:1120 msgid "Copy Image File" msgstr "Copiar Arquivo de Imagem" #: f.file.cc:1121 msgid "Move Image File" msgstr "Mover Arquivo de Imagem" #: f.file.cc:1164 msgid "new location" msgstr "nova localidade" #: f.file.cc:1236 msgid "new location is not a directory" msgstr "nova localidade não é um diretório" #: f.file.cc:1276 f.file.cc:1518 f.file.cc:1656 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "exclusão falhou: \n" " %s" #: f.file.cc:1363 f.widgets.cc:401 msgid "Trash Image File" msgstr "Mover arquivo para lixeira" #: f.file.cc:1364 f.file.cc:1571 msgid "(automatic step to next image)" msgstr "(passar automaticamente para a próxima imagem)" #: f.file.cc:1432 msgid "" "Linux standard trash does not work. \n" "Desktop trash folder will be created." msgstr "" "Lixeira padrão Linux não funciona. \n" "Diretório de lixo será criado na Área de Trabalho." #: f.file.cc:1435 msgid "" "Linux and Desktop trash do not work. \n" "Permanently delete the image file?" msgstr "" "Lixeira padrão Linux e Diretório de lixo não funcionam. \n" "Excluir permanentemente arquivo de imagem?" #: f.file.cc:1438 #, c-format msgid "Cannot create trash folder: %s" msgstr "Não foi possível criar pasta lixeira: %s" #: f.file.cc:1450 msgid "Move read-only file to trash?" msgstr "Mover arquivo de apenas leitura para lixeira?" #: f.file.cc:1505 #, c-format msgid "error: %s" msgstr "erro: %s" #: f.file.cc:1534 f.file.cc:1673 f.file.cc:2508 msgid "no more images" msgstr "imagens esgotadas" #: f.file.cc:1570 msgid "Delete Image File - CANNOT BE REVERSED" msgstr "Excluir arquivo de Imagem - IRREVERSÍVEL" #: f.file.cc:1646 msgid "Delete read-only file?" msgstr "Excluir arquivo somente leitura?" #: f.file.cc:1891 msgid "Save Image File" msgstr "Salvar Arquivo-imagem" #: f.file.cc:1901 msgid "new version" msgstr "nova versão" #: f.file.cc:1902 msgid "new file" msgstr "novo arquivo" #: f.file.cc:1903 msgid "replace file" msgstr "substituir arquivo" #: f.file.cc:1910 f.file.cc:2281 msgid "save as new file name or type" msgstr "salvar com novo nome ou tipo de arquivo" #: f.file.cc:1912 msgid "replace old file (OVERWRITE)" msgstr "substituir arquivo antigo (SOBRESCREVER)" #: f.file.cc:1950 f.file.cc:2057 msgid "cannot save as RAW type" msgstr "não foi possível salvar como RAW" #: f.file.cc:2051 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:2191 msgid "Unable to copy EXIF/IPTC data" msgstr "Incapaz de copiar dados EXIF/IPTC" #: f.file.cc:2299 f.file.cc:2302 msgid "make current" msgstr "tornar atual" #: f.file.cc:2300 msgid "(new file becomes current file)" msgstr "(novo arquivo se torna arquivo atual)" #: f.file.cc:2409 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Sobrescrever arquivo? \n" " %s" #: f.file.cc:2555 f.widgets.cc:554 msgid "Quick Start" msgstr "Guia Rápido" #: f.file.cc:2558 f.widgets.cc:555 msgid "User Guide" msgstr "Guia de usuário" #: f.file.cc:2561 f.widgets.cc:556 msgid "User Guide Changes" msgstr "Mudanças no Guia de Usuários" #: f.file.cc:2564 f.widgets.cc:558 msgid "README" msgstr "LEIAME" #: f.file.cc:2567 f.widgets.cc:557 msgid "Edit Functions Summary" msgstr "Editar Resumo de Funções" #: f.file.cc:2570 f.widgets.cc:559 msgid "Change Log" msgstr "Relatório de mudanças" #: f.file.cc:2573 f.widgets.cc:560 msgid "Log File" msgstr "Arquivo de Log" #: f.file.cc:2576 f.widgets.cc:561 msgid "Translations" msgstr "Traduções" #: f.file.cc:2579 f.widgets.cc:562 msgid "Home Page" msgstr "Página na web" #: f.file.cc:2582 f.widgets.cc:563 msgid "About" msgstr "Sobre" #: f.file.cc:2586 f.widgets.cc:584 f.widgets.cc:606 fotoxx.h:1123 msgid "Help" msgstr "Ajuda" #: f.file.cc:2739 f.file.cc:2808 #, c-format msgid "file not found: %s" msgstr "arquivo não encontrado: %s" #: f.file.cc:2745 f.file.cc:2814 #, c-format msgid "file type not supported: %s" msgstr "tipo de arquivo não suportado: %s" #: f.gallery.cc:918 f.gallery.cc:1007 f.widgets.cc:604 msgid "Scroll" msgstr "Rolar" #: f.gallery.cc:922 msgid "Sync" msgstr "Sinc" #: f.gallery.cc:932 f.repair.cc:5845 f.repair.cc:5850 fotoxx.h:1144 msgid "Open" msgstr "Abrir" #: f.gallery.cc:933 msgid "change directory" msgstr "mudar diretório" #: f.gallery.cc:941 msgid "GoTo" msgstr "IrPara" #: f.gallery.cc:947 f.widgets.cc:599 msgid "Sort" msgstr "Ordenar" #: f.gallery.cc:953 f.gallery.cc:1517 f.gallery.cc:1518 f.gallery.cc:1520 #: f.widgets.cc:597 msgid "Zoom+" msgstr "Zoom+" #: f.gallery.cc:963 f.gallery.cc:1519 f.gallery.cc:1521 f.widgets.cc:598 msgid "Zoom-" msgstr "Zoom-" #: f.gallery.cc:975 f.gallery.cc:1525 msgid "Row↑" msgstr "Linha↑" #: f.gallery.cc:983 f.gallery.cc:1526 msgid "Row↓" msgstr "Linha↓" #: f.gallery.cc:991 f.gallery.cc:1523 f.gallery.cc:1530 f.widgets.cc:602 msgid "Page↑" msgstr "Página↑" #: f.gallery.cc:999 f.gallery.cc:1524 f.gallery.cc:1531 f.widgets.cc:603 msgid "Page↓" msgstr "Página↓" #: f.gallery.cc:1013 f.gallery.cc:1528 f.widgets.cc:600 msgid "First" msgstr "Primeiro" #: f.gallery.cc:1014 f.gallery.cc:1529 f.widgets.cc:601 msgid "Last" msgstr "Último" #: f.gallery.cc:1127 msgid "Choose image directory" msgstr "Escolher diretório de imagens" #: f.gallery.cc:1134 f.gallery.cc:1148 msgid "recent" msgstr "recente" #: f.gallery.cc:1135 f.gallery.cc:1153 msgid "newest" msgstr "mais novo" #: f.gallery.cc:1217 msgid "no albums found" msgstr "nenhum álbum encontrado" #: f.gallery.cc:1224 msgid "Choose album" msgstr "Escolher álbum" #: f.gallery.cc:1274 msgid "Gallery Sort" msgstr "Ordenar Galeria" #: f.gallery.cc:1278 msgid "File Name" msgstr "Nome de Arquivo" #: f.gallery.cc:1279 msgid "File Mod Date/Time" msgstr "Data/Hora da Modificação do Arquivo" #: f.gallery.cc:1280 msgid "Photo Date/Time (EXIF)" msgstr "Foto Data/Hora (EXIF)" #: f.gallery.cc:1282 msgid "ascending" msgstr "ascendente" #: f.gallery.cc:1283 msgid "descending" msgstr "descendente" #: f.gallery.cc:2212 fotoxx.h:1169 msgid "Select Files" msgstr "Selecionar arquivos" #: f.gallery.cc:2672 msgid "Click list position. Click thumbnail to add." msgstr "Clique na posição da lista. Clique na miniatura para adicionar." #: f.gallery.cc:2698 f.gallery.cc:2946 msgid "Edit Bookmarks" msgstr "Editar Favoritos" #: f.gallery.cc:2873 msgid "unable to save bookmarks file" msgstr "incapaz de salvar arquivo de favoritos" #: f.gallery.cc:2946 msgid "Go To Bookmark" msgstr "Ir para Favorito" #: f.gallery.cc:3003 f.meta.cc:2844 f.repair.cc:7473 msgid "file not found" msgstr "arquivo não encontrado" #: 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:241 msgid "choose layout file" msgstr "escolher arquivo de leiaute" #: 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:417 msgid "Edit Images" msgstr "Editar imagens" #: f.mashup.cc:418 f.mashup.cc:2432 msgid "Edit Text" msgstr "Editar texto" #: f.mashup.cc:419 msgid "Edit Line" msgstr "Editar Linha" #: f.mashup.cc:420 msgid "Rescale" msgstr "Reescalar" #: f.mashup.cc:423 msgid "add or edit images" msgstr "adicionar ou editar imagens" #: f.mashup.cc:424 msgid "add or edit text" msgstr "adicionar ou editar texto" #: f.mashup.cc:425 msgid "add or edit lines/arrows" msgstr "adicionar ou editar linhas/setas" #: f.mashup.cc:426 msgid "change project scale" msgstr "mudar escala de projeto" #: f.mashup.cc:427 msgid "project complete" msgstr "projeto finalizado" #: f.mashup.cc:428 msgid "cancel project" msgstr "cancelar projeto" #: f.mashup.cc:446 msgid "rescale project" msgstr "reescalar projeto" #: f.mashup.cc:501 msgid "save Mashup output file" msgstr "salvar arquivo de saída de Mistura" #: f.mashup.cc:512 msgid "save Mashup project file?" msgstr "salvar arquivo de projeto da Mistura?" #: f.mashup.cc:524 msgid "delete Mashup project file?" msgstr "excluir arquivo de projeto da Mistura?" #: f.mashup.cc:583 msgid "Open Project" msgstr "Abrir projeto" #: f.mashup.cc:612 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "arquivo-imagem de leiaute faltando: \n" " %s" #: f.mashup.cc:669 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "arquivo-imagem sobreposta faltando: \n" " %s" #: f.mashup.cc:974 msgid "project file is defective" msgstr "arquivo de projeto está defeituoso" #: f.mashup.cc:1004 msgid "Save Project" msgstr "Salvar projeto" #: f.mashup.cc:1145 msgid "layout exceeds 2 gigabytes" msgstr "layout excede 2 gigabytes" #: f.mashup.cc:1219 msgid "Click image to select, drag image to move." msgstr "Clique na imagem para selecionar, arrastar imagem para mover." #: f.mashup.cc:1220 msgid "Make black margins transparent" msgstr "Tornar margens pretas em transparentes" #: f.mashup.cc:1259 msgid "Current image:" msgstr "Imagem sendo usada:" #: f.mashup.cc:1263 msgid "Cycle through images:" msgstr "Período entre imagens:" #: f.mashup.cc:1270 msgid "Scale" msgstr "Escala" #: f.mashup.cc:1279 msgid "Stacking Order" msgstr "Ordem de Empilhamento" #: f.mashup.cc:1280 msgid "raise" msgstr "aumentar" #: f.mashup.cc:1281 msgid "lower" msgstr "diminuir" #: f.mashup.cc:1284 msgid "Base Transparency" msgstr "Transparência Base" #: f.mashup.cc:1288 msgid "Var. Transparency" msgstr "Transparência Variável" #: f.mashup.cc:1289 msgid "Paint" msgstr "Pintura" #: f.mashup.cc:1292 msgid "Bend and fine-align" msgstr "Curvatura e ajuste fino" #: f.mashup.cc:1293 msgid "Warp" msgstr "Distorção" #: f.mashup.cc:1302 msgid "Margins" msgstr "Margens" #: f.mashup.cc:1303 msgid "Hard" msgstr "Rígido" #: f.mashup.cc:1304 f.repair.cc:5441 msgid "Blend" msgstr "Mistura" #: f.mashup.cc:1318 msgid "add images to layout" msgstr "adicionar imagens ao leiaute" #: f.mashup.cc:1557 msgid "Paint Image Transparencies" msgstr "Aplicar Transparências na Imagem" #: f.mashup.cc:1575 msgid "Gradual" msgstr "Gradual" #: f.mashup.cc:1580 fotoxx.h:1148 msgid "Power" msgstr "Força" #: f.mashup.cc:1814 msgid "Pull on the image with the mouse." msgstr "Puxe a imagem com o mouse." #: f.mashup.cc:1830 msgid "Warp Image" msgstr "Distorcer Imagem" #: f.mashup.cc:2263 #, c-format msgid "exceeded %d images" msgstr "excederam%d imagens" #: f.mashup.cc:2405 msgid "Enter text, [Add] to layout, edit properties." msgstr "Digite o texto, [Adicionar] para o leiaute, editar propriedades." #: f.mashup.cc:2485 msgid "Text File:" msgstr "Arquivo Texto:" #: f.mashup.cc:2489 msgid "add entered text to layout" msgstr "adicionar texto inserido ao leiaute" #: f.mashup.cc:2618 msgid "click position to add text" msgstr "clique na posição para adicionar texto" #: f.mashup.cc:2624 #, c-format msgid "exceeded %d text entries" msgstr "excederam %d entradas de texto" #: f.mashup.cc:2747 msgid "Set line properties, [Add] to layout, edit." msgstr "Definir propriedades de linha, [Adicionar] ao leiaute, editar." #: f.mashup.cc:2771 msgid "Edit Line/Arrow" msgstr "Editar Linha/Seta" #: f.mashup.cc:2826 msgid "add line/arrow to layout" msgstr "adicionar linha/seta ao leiaute" #: f.mashup.cc:2919 msgid "click position to add line" msgstr "clique na posição para adicionar linha" #: f.mashup.cc:2925 #, c-format msgid "exceeded %d line entries" msgstr "excederam%d linhas de entrada" #: f.meta.cc:180 f.widgets.cc:669 msgid "View Metadata" msgstr "Ver metadados" #: f.meta.cc:430 f.meta.cc:1807 f.widgets.cc:412 f.widgets.cc:670 msgid "Edit Metadata" msgstr "Editar Metadados" #: f.meta.cc:433 f.meta.cc:3819 msgid "save metadata to file" msgstr "salvar metadados em arquivo" #: f.meta.cc:442 msgid "Image Date" msgstr "Data da Imagem" #: f.meta.cc:445 msgid "Time" msgstr "Hora" #: f.meta.cc:451 msgid "Rating (stars):" msgstr "Avaliação (estrelas):" #: f.meta.cc:463 msgid "Caption" msgstr "Legenda" #: f.meta.cc:469 msgid "Comments" msgstr "Comentários" #: f.meta.cc:477 msgid "Enter New Tag" msgstr "Entre com Nova Etiqueta" #: f.meta.cc:484 f.meta.cc:5003 msgid "Matching Tags" msgstr "Etiquetas Correspondentes" #: f.meta.cc:492 msgid "Image Tags" msgstr "Etiquetas da imagem" #: f.meta.cc:498 msgid "Recent Tags" msgstr "etiquetas recentes" #: f.meta.cc:507 f.meta.cc:2029 f.meta.cc:5010 msgid "Defined Tags Category" msgstr "Categorias de Etiquetas Definidas" #: f.meta.cc:898 msgid "date format is YYYY-MM-DD" msgstr "formato da data é AAAA-MM-DD" #: f.meta.cc:902 msgid "date is invalid" msgstr "data inválida" #: f.meta.cc:936 msgid "time format is HH:MM [:SS]" msgstr "formato da hora é HH:MM [: SS]" #: f.meta.cc:940 msgid "time is invalid" msgstr "hora inválida" #: f.meta.cc:1025 fotoxx.h:1133 msgid "Manage Tags" msgstr "Gerenciar Etiquetas" #: f.meta.cc:1025 msgid "orphan tags" msgstr "etiquetas órfãs" #: f.meta.cc:1029 msgid "category" msgstr "categoria" #: f.meta.cc:1032 msgid "tag" msgstr "etiqueta" #: f.meta.cc:1039 msgid "Defined Tags:" msgstr "etiquetas definidas:" #: f.meta.cc:1811 f.meta.cc:2474 msgid "key name" msgstr "nome chave" #: f.meta.cc:1813 f.meta.cc:2475 msgid "key value" msgstr "valor chave" #: f.meta.cc:1900 f.widgets.cc:414 msgid "Delete Metadata" msgstr "Excluir Metadados" #: f.meta.cc:1902 fotoxx.h:1082 msgid "All" msgstr "Todos" #: f.meta.cc:1903 msgid "One Key:" msgstr "Uma chave:" #: f.meta.cc:2006 f.widgets.cc:415 f.widgets.cc:540 msgid "Batch Add/Remove Tags" msgstr "Adicionar/Remover Etiqueras em Lote" #: f.meta.cc:2017 msgid "tags to add" msgstr "etiquetas a adicionar" #: f.meta.cc:2018 msgid "tags to remove" msgstr "etiquetas a remover" #: f.meta.cc:2102 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " etiquetas demasiadas" #: f.meta.cc:2217 msgid "specify files and tags" msgstr "especificar arquivos e etiquetas" #: f.meta.cc:2277 msgid "tag names file" msgstr "arquivo de nomes de etiqueta" #: f.meta.cc:2349 #, 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:2461 msgid "Batch Metadata" msgstr "Metadados em Lote" #: f.meta.cc:2468 msgid "Short List" msgstr "Lista Reduzida" #: f.meta.cc:2469 msgid "Full List" msgstr "Lista Completa" #: f.meta.cc:2511 msgid "enter key names" msgstr "inserir nome de chave" #: f.meta.cc:2517 fotoxx.h:1141 msgid "no files selected" msgstr "nenhum arquivo selecionado" #: f.meta.cc:2643 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:3276 f.meta.cc:4011 f.meta.cc:4277 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "latitude/longitude inválido: %s %s" #: f.meta.cc:3320 f.meta.cc:3682 f.meta.cc:4486 f.meta.cc:4894 msgid "-noindex in use, disabled" msgstr "-noindex em uso, desabilitado" #: f.meta.cc:3336 msgid "choose map file" msgstr "escolher arquivo de mapa" #: f.meta.cc:3444 msgid "" "fotoxx-maps package not installed \n" "(see http://kornelix.com/packages and /tarballs)" msgstr "" "pacote fotoxx-maps não instalado \n" "(http://kornelix.com/packages e /tarballs)" #: f.meta.cc:3449 #, c-format msgid "map file %s is missing" msgstr "arquivo de mapa file %s faltando" #: f.meta.cc:3453 #, 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:3731 msgid "No matching images found" msgstr "Nenhuma imagem encontrada" #: f.meta.cc:3771 msgid "search range (km)" msgstr "procurar distância (km)" #: f.meta.cc:3804 f.widgets.cc:418 f.widgets.cc:671 msgid "Edit Geotags" msgstr "Editar Georreferências" #: f.meta.cc:3805 msgid "Geocoding web service courtesy of" msgstr "Serviço de geocodificação web cortesia de" #: f.meta.cc:3821 f.meta.cc:4060 f.meta.cc:5783 msgid "city" msgstr "cidade" #: f.meta.cc:3824 f.meta.cc:4063 f.meta.cc:5786 msgid "country" msgstr "país" #: f.meta.cc:3906 f.meta.cc:4194 msgid "city not found" msgstr "cidade não encontrada" #: f.meta.cc:4027 f.widgets.cc:419 f.widgets.cc:543 msgid "Batch Add Geotags" msgstr "Adicionar Georreferências em Lote" #: f.meta.cc:4256 msgid "" "data is incomplete \n" " proceed?" msgstr "" "Dados incompletos \n" " continuar?" #: f.meta.cc:4313 msgid "choose city" msgstr "escolher cidade" #: f.meta.cc:4400 msgid "not found" msgstr "não encontrado" #: f.meta.cc:4401 msgid "city and country required" msgstr "cidade e país requerido" #: f.meta.cc:4501 msgid "Report Geotag Groups" msgstr "Relatório de Grupos de Georreferências" #: f.meta.cc:4502 msgid "Group by country" msgstr "Agrupar por país" #: f.meta.cc:4503 msgid "Group by country/city" msgstr "Agrupar por país/cidade" #: f.meta.cc:4504 msgid "Group by country/city/date" msgstr "Agrupar por país/cidade/data" #: f.meta.cc:4507 msgid "Combine within" msgstr "Combinar interiormente" #: f.meta.cc:4509 msgid "days" msgstr "dias" #: f.meta.cc:4625 msgid "geotag groups" msgstr "grupos de georreferência" #: f.meta.cc:4931 msgid "Search Image Metadata" msgstr "Procurar por Metadados de Imagem" #: f.meta.cc:4935 msgid "images to search:" msgstr "imagens para busca:" #: f.meta.cc:4936 msgid "all" msgstr "todos" #: f.meta.cc:4937 msgid "current set only" msgstr "configuração atual apenas" #: f.meta.cc:4940 msgid "matching images:" msgstr "imagens correspondentes:" #: f.meta.cc:4941 msgid "new set" msgstr "nova configuração" #: f.meta.cc:4942 msgid "add to set" msgstr "adicionar à configuração" #: f.meta.cc:4943 msgid "remove" msgstr "remover" #: f.meta.cc:4946 msgid "report type:" msgstr "tipo relatório:" #: f.meta.cc:4947 msgid "gallery" msgstr "galeria" #: f.meta.cc:4948 fotoxx-15.11.cc:791 msgid "metadata" msgstr "metadados" #: f.meta.cc:4954 msgid "date range" msgstr "variação de data" #: f.meta.cc:4955 msgid "stars range" msgstr "variação de estrelas" #: f.meta.cc:4956 msgid "search tags" msgstr "procurar etiquetas" #: f.meta.cc:4957 msgid "search text" msgstr "procurar texto" #: f.meta.cc:4958 msgid "search files" msgstr "procurar arquivos" #: f.meta.cc:4963 msgid "(yyyymmdd)" msgstr "(aaaammdd)" #: f.meta.cc:4968 msgid "last version only" msgstr "última versão apenas" #: f.meta.cc:4970 msgid "all/any" msgstr "todos/qualquer" #: f.meta.cc:4989 msgid "other criteria" msgstr "outro critério" #: f.meta.cc:4993 msgid "other" msgstr "outro" #: f.meta.cc:4999 msgid "Enter Search Tag" msgstr "Entre com a Etiqueta de Busca" #: f.meta.cc:5294 #, c-format msgid "not a defined tag: %s" msgstr "não é etiqueta definida: %s" #: f.meta.cc:5325 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:5332 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:5376 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "datas de procura não fazem sentido \n" " %s %s" #: f.meta.cc:5394 msgid "stars range not reasonable" msgstr "varição de avaliação não é razoável" #: f.meta.cc:5639 #, c-format msgid "images added: %d removed: %d new count: %d" msgstr "imagens adicionadas: %d removidas: %d nova contagem: %d" #: f.meta.cc:5642 msgid "no changes made" msgstr "nenhuma mudança feita" #: f.meta.cc:5780 msgid "Add Geotags Search Criteria" msgstr "Adicionar Critério de Busca Georreferencial" #: f.meta.cc:5799 msgid "range (km)" msgstr "alcance (km)" #: f.meta.cc:5892 msgid "error in latitude/longitude/range" msgstr "erro na latitude/longitude/distância" #: f.meta.cc:5956 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:5980 msgid "Additional Items for Report" msgstr "Itens Adicionais para Relatar" #: f.meta.cc:5986 msgid "Keyword" msgstr "Palavra chave" #: f.meta.cc:5993 msgid "Match Criteria" msgstr "Equiparar Critério" #: f.meta.cc:6553 msgid "image index is missing" msgstr "índice de imagem faltando" #: f.repair.cc:1041 msgid "Apply repeatedly while watching the image." msgstr "Aplicar repetidamente enquanto visualiza a imagem." #: f.repair.cc:1042 msgid "Measure" msgstr "Mensurar" #: f.repair.cc:1078 msgid "Noise Reduction" msgstr "Redução de ruído" #: f.repair.cc:1099 msgid "Flatten 1" msgstr "Achatamento 1" #: f.repair.cc:1100 msgid "Flatten 2" msgstr "Achatamento 2" #: f.repair.cc:1101 msgid "Median" msgstr "Mediano" #: f.repair.cc:1121 msgid "dark areas" msgstr "áreas escuras" #: f.repair.cc:1123 msgid "all areas" msgstr "todas as áreas" #: f.repair.cc:1246 msgid "Measure Noise" msgstr "Mensurar Ruído" #: f.repair.cc:1247 msgid "Click on a monotone image area." msgstr "Clique em uma área monocórdica na imagem." #: f.repair.cc:2177 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:2199 f.widgets.cc:457 msgid "Smart Erase" msgstr "Apagador inteligente" #: f.repair.cc:2204 fotoxx.h:1152 msgid "Radius" msgstr "Raio" #: f.repair.cc:2206 f.widgets.cc:455 msgid "Blur" msgstr "Embaçar" #: f.repair.cc:2209 msgid "New Area" msgstr "Nova Área" #: f.repair.cc:2558 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:2574 msgid "Red Eye Reduction" msgstr "Redução de olhos vermelhos" #: f.repair.cc:3038 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 "" "shift + clique esquerdo: escolher cor ou posição da imagem \n" "clique esquerdo ou arrastar: cor da pintura ou copiar imagem \n" "clique direito ou arrastar: remover cor ou imagem" #: f.repair.cc:3070 f.widgets.cc:459 msgid "Paint/Clone" msgstr "Pintar Pixels" #: f.repair.cc:3074 msgid "paint color" msgstr "cor da pintura" #: f.repair.cc:3077 msgid "copy from image" msgstr "copiar da imagem" #: f.repair.cc:3084 f.repair.cc:3641 msgid "paintbrush radius" msgstr "raio do pincel" #: f.repair.cc:3085 msgid "transparency center" msgstr "centro de transparência" #: f.repair.cc:3086 msgid "transparency edge" msgstr "borda de transparência" #: f.repair.cc:3093 f.repair.cc:3648 msgid "gradual paint" msgstr "pintura gradual" #: f.repair.cc:3100 #, c-format msgid "Undo Memory %d%c" msgstr "Desfazer memória %d%c" #: f.repair.cc:3436 msgid "" "Undo memory limit has been reached. \n" "Save work with [done], then resume painting." msgstr "" "Limite de memória para Desfazer chegou ao limite. \n" "Salve o trabalho com [feito], depois retorne à pintura." #: f.repair.cc:3601 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:3633 f.widgets.cc:460 msgid "Paint Transparency" msgstr "Aplicar Transparência" #: f.repair.cc:3642 msgid "strength center" msgstr "fortalecer centro" #: f.repair.cc:3643 msgid "strength edge" msgstr "fortalecer borda" #: f.repair.cc:3858 f.widgets.cc:461 msgid "Color Mode" msgstr "Modos de Cores" #: f.repair.cc:3862 msgid "black/white positive" msgstr "preto/branco positivo" #: f.repair.cc:3863 msgid "black/white negative" msgstr "preto/branco negativo" #: f.repair.cc:3864 msgid "color positive" msgstr "cor positiva" #: f.repair.cc:3865 msgid "color negative" msgstr "cor negativa" #: f.repair.cc:3866 msgid "sepia" msgstr "sepia" #: f.repair.cc:4046 f.widgets.cc:462 msgid "Shift Colors" msgstr "Deslocar Cores" #: f.repair.cc:4317 msgid "+Brightness" msgstr "+Brilho" #: f.repair.cc:4318 msgid "+Red -Cyan" msgstr "+Vermelho -Ciano" #: f.repair.cc:4319 msgid "+Green -Magenta" msgstr "+Verde -Magenta" #: f.repair.cc:4320 msgid "+Blue -Yellow" msgstr "+Azul -Amarelo" #: f.repair.cc:4326 fotoxx.h:1154 msgid "Red" msgstr "Vermelho" #: f.repair.cc:4327 fotoxx.h:1120 msgid "Green" msgstr "Verde" #: f.repair.cc:4328 fotoxx.h:1089 msgid "Blue" msgstr "Azul" #: f.repair.cc:4623 msgid "Color to change" msgstr "Cor a mudar" #: f.repair.cc:4625 msgid "shift+click on image to select color" msgstr "shift+clique na imagem para selecionar cor" #: f.repair.cc:4638 msgid "Color Hue" msgstr "Matiz de Cor" #: f.repair.cc:4639 msgid "Saturation" msgstr "Saturação" #: f.repair.cc:4640 msgid "Lightness" msgstr "Luminosidade" #: f.repair.cc:4641 msgid "Color Match" msgstr "Correspondência de Cor" #: f.repair.cc:4642 msgid "Adjustment" msgstr "Ajuste" #: f.repair.cc:5028 msgid "Ramp brightness across image" msgstr "Curva de Brilho sobre a imagem" #: f.repair.cc:5352 f.tools.cc:2048 msgid "Click image to select pixels." msgstr "Clique na imagem para selecionar pixels." #: f.repair.cc:5383 f.widgets.cc:466 msgid "Color Ramp" msgstr "Paleta de Cores" #: f.repair.cc:5394 msgid "Metric:" msgstr "Métrica:" #: f.repair.cc:5817 msgid "Color Match Images" msgstr "Imagens para mesclagem de cor" #: f.repair.cc:5843 msgid "mouse radius for color sample" msgstr "raio do mouse para a amostra de cor" #: f.repair.cc:5846 msgid "image for source color" msgstr "imagem para fonte de cor" #: f.repair.cc:5848 msgid "click on image to get source color" msgstr "clique na imagem para obter a cor fonte" #: f.repair.cc:5851 msgid "image to set matching color" msgstr "imagem para mesclagem de cor" #: f.repair.cc:5853 msgid "click on image to set matching color" msgstr "clique na imagem para aplicar mesclagem de cor" #: f.repair.cc:5914 msgid "select source image color first" msgstr "selecione imagem para fonte de cor primeiro" #: f.repair.cc:6099 msgid "Change Color Profile" msgstr "Mudar Perfil de Cores" #: f.repair.cc:6103 msgid "input profile" msgstr "perfil de entrada" #: f.repair.cc:6107 msgid "output profile" msgstr "perfil de saída" #: f.repair.cc:6123 msgid "color profile" msgstr "perfil de cor" #: f.repair.cc:6169 f.repair.cc:6175 #, c-format msgid "unknown cms profile %s" msgstr "perfil cms desconhecido %s" #: f.repair.cc:6307 f.widgets.cc:469 msgid "Remove Dust" msgstr "Remover Sujeira" #: f.repair.cc:6311 msgid "spot size limit" msgstr "limite de tamanho de mancha" #: f.repair.cc:6314 msgid "max. brightness" msgstr "brilho máximo" #: f.repair.cc:6317 msgid "min. contrast" msgstr "contraste mínimo" #: f.repair.cc:7098 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:7121 f.widgets.cc:471 msgid "Color Fringes" msgstr "Franjas Púrpuras" #: f.repair.cc:7320 f.widgets.cc:472 msgid "Stuck Pixels" msgstr "Pixels Presos" #: f.repair.cc:7326 msgid "pixel group" msgstr "grupo de pixels" #: f.repair.cc:7334 f.repair.cc:7402 msgid "stuck pixels:" msgstr "pixels presos:" #: f.repair.cc:7430 msgid "Load Stuck Pixels" msgstr "Carregar Pixels Presos" #: f.repair.cc:7432 msgid "File:" msgstr "Arquivo:" #: f.repair.cc:7457 f.repair.cc:7514 msgid "Stuck Pixels file" msgstr "Arquvio de Pixels Presos" #: f.repair.cc:7494 msgid "file format error" msgstr "erro de formato de arquivo" #: f.tools.cc:70 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:72 msgid "Select to add, click on X to delete." msgstr "Selecione para adicionar, clique em X para apagar." #: f.tools.cc:73 msgid "Select directory for thumbnails." msgstr "Selecione um diretório para miniaturas." #: f.tools.cc:74 msgid "" "Index function terminated. Fotoxx will close. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Função de indexação finalizada. Fotoxx vai se fechar. \n" "A indexação é necessário para funções de busca e mapeamento \n" "e para tornar as páginas da galeria aceitavelmente ágeis." #: f.tools.cc:106 f.widgets.cc:513 msgid "Index Image Files" msgstr "Arquivos de Índice de imagens" #: f.tools.cc:250 msgid "Choose top image directories" msgstr "Selecionar pasta raiz de imagens" #: f.tools.cc:251 msgid "Choose thumbnail directory" msgstr "Escolher diretório de miniaturas" #: f.tools.cc:324 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:330 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" "Diretório inválido: \n" " %s \n" "Por favor, remova." #: f.tools.cc:333 #, 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:336 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" "Diretório duplicado: \n" " %s \n" "Por favor, remova." #: f.tools.cc:385 msgid "no thumbnails directory defined" msgstr "nenhum diretório de miniaturas definido" #: f.tools.cc:735 msgid "COMPLETED" msgstr "COMPLETO" #: f.tools.cc:776 msgid "Indexing is required for first-time startup." msgstr "A indexação é necessário na primeira inicialização." #: f.tools.cc:838 msgid "Recent Files Gallery" msgstr "Arquivos Recentes da Galeria" #: f.tools.cc:839 msgid "Newest Files Gallery" msgstr "Galeria de Arquivos Recentes" #: f.tools.cc:840 msgid "Previous Gallery" msgstr "Galeria Anterior" #: f.tools.cc:841 msgid "Previous Image" msgstr "Imagem Anterior" #: f.tools.cc:842 msgid "Blank Window" msgstr "Janela em Branco" #: f.tools.cc:843 msgid "Directory" msgstr "Diretório" #: f.tools.cc:844 msgid "Image File" msgstr "Arquivo de Imagem" #: f.tools.cc:887 f.widgets.cc:514 msgid "User Options" msgstr "Opções de Usuário" #: f.tools.cc:890 msgid "Startup Display" msgstr "Exibição Inicial" #: f.tools.cc:901 msgid "Menu Style" msgstr "Estilo de Menu" #: f.tools.cc:902 msgid "Icons" msgstr "Ícones" #: f.tools.cc:903 msgid "Icons + Text" msgstr "Ícones + Texto" #: f.tools.cc:905 msgid "Icon size" msgstr "Tamanho de ícone" #: f.tools.cc:909 msgid "Dialog font and size" msgstr "Tamanho de fonte da caixa de Diálogo" #: f.tools.cc:914 msgid "Image Pan/scroll:" msgstr "Deslocamento de Imagem Horizontal:" #: f.tools.cc:917 msgid "Zooms for 2x" msgstr "Zoom para 2x" #: f.tools.cc:926 msgid "JPEG save quality" msgstr "Qualidade para JPEG" #: f.tools.cc:933 msgid "Curve node capture distance" msgstr "Distância de captura de nó da curva" #: f.tools.cc:937 msgid "show hidden directories in gallery view" msgstr "mostrar diretórios ocultos na visão de galeria" #: f.tools.cc:940 msgid "prev/next shows last file version only" msgstr "prox./ant. mostra apenas a última versão do arquivo" #: f.tools.cc:943 msgid "RAW command" msgstr "comando RAW" #: f.tools.cc:947 msgid "RAW file types" msgstr "Tipos de arquivos RAW" #: f.tools.cc:1029 msgid "Delete present thumbnails to make effective" msgstr "Excluir miniaturas presentes para efetivar" #: f.tools.cc:1051 msgid "Select startup directory" msgstr "Selecionar diretório de inicialização" #: f.tools.cc:1058 msgid "Select startup image file" msgstr "Selecionar arquivo de imagem inicial" #: f.tools.cc:1096 msgid "startup directory is invalid" msgstr "diretório de iniciação é inválido" #: f.tools.cc:1107 msgid "startup file is invalid" msgstr "arquivo de iniação é inválido" #: f.tools.cc:1247 msgid "Edit KB Shortcuts" msgstr "Editar Atalhos" #: f.tools.cc:1252 msgid "shortcut key:" msgstr "tecla de atalho:" #: f.tools.cc:1253 msgid "(enter key)" msgstr "(tecla enter)" #: f.tools.cc:1379 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Reservado, não pode ser usado" #: f.tools.cc:1533 msgid "unable to save KB-shortcuts file" msgstr "incapaz de salvar arquivo de atalhos do teclado" #: f.tools.cc:1837 f.widgets.cc:517 msgid "Grid Lines" msgstr "Linhas de grade" #: f.tools.cc:1846 msgid "x-spacing" msgstr "espaçamento-x" #: f.tools.cc:1847 msgid "x-count" msgstr "contagem-x" #: f.tools.cc:1848 msgid "x-enable" msgstr "x-habilitado" #: f.tools.cc:1854 msgid "y-spacing" msgstr "espaçamento-y" #: f.tools.cc:1855 msgid "y-count" msgstr "contagem-y" #: f.tools.cc:1856 msgid "y-enable" msgstr "y-habilitado" #: f.tools.cc:1976 f.tools.cc:1981 f.widgets.cc:518 msgid "Line Color" msgstr "Cor da linha" #: f.tools.cc:1978 msgid "Area Color" msgstr "Cor da área" #: f.tools.cc:2083 f.widgets.cc:519 msgid "Show RGB" msgstr "Mostrar RGB" #: f.tools.cc:2375 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key M to toggle dialog." msgstr "" "Arraste o mouse na imagem. \n" "Clique com botão esquerdo para cancelar. \n" "Tecla M alterna a caixa de diálogo." #: f.tools.cc:2401 f.widgets.cc:520 msgid "Magnify Image" msgstr "Ampliar Imagens" #: f.tools.cc:2410 msgid "X-size" msgstr "Tamanho-X" #: f.tools.cc:2655 msgid "Darkest and Brightest Pixels" msgstr "Pixels mais Escuros e Brilhantes" #: f.tools.cc:2678 msgid "Dark Limit" msgstr "Limite Escuro" #: f.tools.cc:2679 msgid "Bright Limit" msgstr "Limite Claro" #: f.tools.cc:2774 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:2870 f.widgets.cc:523 msgid "Monitor Gamma" msgstr "Monitor Gamma" #: f.tools.cc:2930 msgid "Available Translations" msgstr "Traduções disponíveis" #: f.tools.cc:2934 msgid "Set Language" msgstr "Configurar língua" #: f.tools.cc:3071 f.widgets.cc:526 msgid "Calibrate Printer" msgstr "Calibrar Impressora" #: f.tools.cc:3096 msgid "print color chart" msgstr "imprimir gráfico de cores" #: f.tools.cc:3097 msgid "scan and save color chart" msgstr "digitalizar e salvar gráfico de cores" #: f.tools.cc:3098 msgid "align and trim color chart" msgstr "alinhar e aparar gráfico de cores" #: f.tools.cc:3099 msgid "open and process color chart" msgstr "abrir e processar gráfico de cores" #: f.tools.cc:3100 msgid "print image with revised colors" msgstr "imprimir imagem com cores revisadas" #: f.tools.cc:3264 #, c-format msgid "" "Scan the printed color chart. \n" "Save in %s/" msgstr "" "Digitalizar o gráfico de cores impresso. \n" "Salvar em %s/" #: f.tools.cc:3278 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation caused by the scanner. \n" "Trim off all margins. Be careful not to cut off \n" "any color tile edges or leave any white margins." msgstr "" "Abrir e editar o arquivo do gráfico de cores digitalizado. \n" "Remover qualquer inclinação ou rotação causado pelo digitalizador. \n" "Aparar todas as margens. Tenha cuidado para não retirar \n" "nenhuma cor da borda das figuras ou deixar qualquer margens brancas." #: f.tools.cc:3310 msgid "Open the trimmed color chart file" msgstr "Abrir arquivo de gráfico de cores aparado" #: f.tools.cc:3443 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:3483 msgid "Color map file to use" msgstr "Arquivo de mapa de cores a usar" #: f.tools.cc:3500 msgid "Select the image file to print." msgstr "Selecione o arquivo de imagem para imprimir." #: f.tools.cc:3565 msgid "converting colors..." msgstr "convertendo cores..." #: f.tools.cc:3665 msgid "Image colors are converted for printing." msgstr "Cores da imagem foram convertidas para impressão." #: f.widgets.cc:111 msgid "Album" msgstr "Álbum" #: f.widgets.cc:113 msgid "TOP" msgstr "TOPO" #: f.widgets.cc:165 msgid "Current Image File (key F)" msgstr "Arquivo de Imagem atual (tecla F)" #: f.widgets.cc:166 msgid "Thumbnail Gallery (key G)" msgstr "Miniatura de Galeria (tecla G)" #: f.widgets.cc:167 msgid "World Maps (key W)" msgstr "Mapa Mundial (tecla W)" #: f.widgets.cc:170 msgid "Favorite Functions" msgstr "Funções Favoritas" #: f.widgets.cc:171 msgid "File: Open, RAW, Rename, Trash, Print" msgstr "Arquivo: Abrir, RAW, Renomear, Descartar, Imprimir" #: f.widgets.cc:172 msgid "Save modified image file to disk" msgstr "Salvar arquivo-imagem modificado no disco " #: f.widgets.cc:173 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:174 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "Metadados: Legendas, Etiquetas, Avaliações, Georreferências, Busca..." #: f.widgets.cc:175 msgid "Areas: Select areas to edit, copy and paste" msgstr "Áreas: Selecionar área para editar, copiar e colar" #: f.widgets.cc:176 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "" "Editar: Recortar, Rotacionar, Redimensionar, Brilho, Contraste, Texto..." #: f.widgets.cc:177 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "Reparo: Aguçar, Ruído, Olhos Vermelhos, Colorir, Pintar, Clonar..." #: f.widgets.cc:178 msgid "Bend: Fix Perspective, Bend/Warp image ..." msgstr "Curvar: Corrigir perspectiva, Curvar/Distorcer imagem..." #: f.widgets.cc:179 msgid "Effects: Special Effects, Arty Transforms" msgstr "Efeitos: Efeitos especiais, Transformações Artísticas" #: f.widgets.cc:180 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "Combinação: HDR, HDF, Panorama, Empilhamento, Mistura" #: f.widgets.cc:181 msgid "" "Undo or Redo one edit (left/right mouse click) \n" " hold key A to include all edits" msgstr "" "Desfazer ou Refazer uma edição (clique direito/esquerdo do mouse) \n" " segure a tecla A para incluir todas as edições" #: f.widgets.cc:183 msgid "Tools: Index, Options, Shortcuts, Magnify ..." msgstr "Ferramentas: Índice, Opções, Atalhos, Ampliar ..." #: f.widgets.cc:184 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "Ajuda: Guia Rápido, Guia de Usuário, Mudanças Recentes..." #: f.widgets.cc:187 msgid "Set gallery from current image file" msgstr "Definir galeria a partir de imagem atual" #: f.widgets.cc:188 msgid "Albums: Manage Albums, Slide Show, Desktop Wallpaper" msgstr "Álbuns: Gerenciar Álbuns, Apresentação de Slides, Papel de Parede" #: f.widgets.cc:189 msgid "go to bookmarked image" msgstr "Ir para a imagem marcada como favorita" #: f.widgets.cc:190 msgid "increase thumbnail size" msgstr "aumentar tamanho de miniaturas" #: f.widgets.cc:191 msgid "reduce thumbnail size" msgstr "reduzir tamanho de miniaturas" #: f.widgets.cc:192 msgid "change sort order" msgstr "alterar a ordem de classificação" #: f.widgets.cc:193 msgid "jump to beginning (top)" msgstr "saltar para o início (topo)" #: f.widgets.cc:194 msgid "jump to end (bottom)" msgstr "saltar para final (rodapé)" #: f.widgets.cc:195 msgid "previous page" msgstr "página anterior" #: f.widgets.cc:196 msgid "next page" msgstr "próxima página" #: f.widgets.cc:197 msgid "slow scroll" msgstr "rolar lentamente" #: f.widgets.cc:198 msgid "Batch conversion, metadata, RAW processing" msgstr "Conversões em Lote, metadados, processamento RAW" #: f.widgets.cc:201 msgid "Choose a map for image locations" msgstr "Escolher um mapa para localidades de imagem" #: f.widgets.cc:202 msgid "Set image search radius for map click" msgstr "Definir raio de busca de imagem por clique no mapa" #: f.widgets.cc:205 msgid "Open another window" msgstr "Abrir outra Janela" #: f.widgets.cc:206 msgid "Open a new image file" msgstr "Abrir novo arquvio de imagem" #: f.widgets.cc:207 msgid "Open the previously seen file" msgstr "Abrir o arquivo visto anteriormente" #: f.widgets.cc:208 msgid "Open a recently seen file" msgstr "Abrir um arquivo visto recentemente" #: f.widgets.cc:209 msgid "Open a newly added file" msgstr "Abrir arquivo adicionado recentemente" #: f.widgets.cc:210 f.widgets.cc:211 msgid "Open and edit a camera RAW file" msgstr "Abrir e editar um arquivo RAW da câmera" #: f.widgets.cc:212 msgid "Change the image file name" msgstr "Mudar nome do arquivo" #: f.widgets.cc:213 msgid "Create a blank image" msgstr "Criar uma imagem em branco" #: f.widgets.cc:214 msgid "Move image file to Trash" msgstr "Mover arquivo para Lixeira" #: f.widgets.cc:215 msgid "Permanently delete image file" msgstr "Excluir permanentemente arquivo de imagem" #: f.widgets.cc:216 msgid "Print the current image" msgstr "Imprimir imagem atual" #: f.widgets.cc:217 msgid "Print current image with adjusted colors" msgstr "Imprimir imagem atual com cores ajustadas" #: f.widgets.cc:218 f.widgets.cc:406 msgid "Quit Fotoxx" msgstr "Fechar Fotoxx" #: f.widgets.cc:221 msgid "List a few key metadata items" msgstr "Listar poucos ítens de metadados chave" #: f.widgets.cc:222 msgid "List all metadata items" msgstr "Listar todos ítens de metadados" #: f.widgets.cc:223 msgid "(Toggle) show captions and comments" msgstr "(Comutativo) mostrar legendas e comentários" #: f.widgets.cc:224 msgid "Edit image tags/caption/rating ..." msgstr "Editar etiquetas de imagem/legendas/avaliações ..." #: f.widgets.cc:225 msgid "Edit any image metadata" msgstr "Edita qualquer metadados de uma imagem" #: f.widgets.cc:226 msgid "Remove all metadata from an image" msgstr "Remover todos metadados de uma imagem" #: f.widgets.cc:227 msgid "Add/remove tags for multiple images" msgstr "Adicionar/remover etiquetas para múltiplas imagens" #: f.widgets.cc:228 msgid "Convert tag names for all images" msgstr "Converter nome de etiqueta para todas as imagens" #: f.widgets.cc:229 msgid "Add/change/delete metadata for multiple images" msgstr "Adicionar/Mudar/Excluir metadados em várias imagens" #: f.widgets.cc:230 msgid "Edit image location and geotags" msgstr "Editar localidade de imagem e georreferências" #: f.widgets.cc:231 msgid "Add/revise geotags for multiple images" msgstr "Adicionar/Revisar georeferências para várias imagens" #: f.widgets.cc:232 msgid "Find all images for a location [date]" msgstr "Encontrar todas imagens por uma localidade [data]" #: f.widgets.cc:233 msgid "Find images meeting select criteria" msgstr "Encontrar imagens satisfazendo critério selecionado" #: f.widgets.cc:236 msgid "Select object or area for editing" msgstr "Selecionar objeto ou área para edição" #: f.widgets.cc:237 msgid "Show (outline) existing area" msgstr "Mostrar (traçado) de área existente" #: f.widgets.cc:238 msgid "Hide existing area" msgstr "Esconder área existente" #: f.widgets.cc:239 msgid "Enable area for editing" msgstr "Habilitar área para edição" #: f.widgets.cc:240 msgid "Disable area for editing" msgstr "Desabilitar área para edição" #: f.widgets.cc:241 msgid "Reverse existing area" msgstr "Inverter área existente" #: f.widgets.cc:242 msgid "Erase existing area" msgstr "Apagar área existente" #: f.widgets.cc:243 msgid "Copy area for later pasting into image" msgstr "Copiar área para posterior colagem em imagem" #: f.widgets.cc:244 msgid "Paste previously copied area into image" msgstr "Colar área previamente copiada em imagem" #: f.widgets.cc:245 msgid "Open a file and paste as area into image" msgstr "Abrir um arquivo e colar como área na imagem" #: f.widgets.cc:246 msgid "Save area to a file with transparency" msgstr "Salvar área em arquivo com transparência" #: f.widgets.cc:249 msgid "Trim/Crop margins and/or Rotate" msgstr "Recortar/Aparar margens e/ou Rodar" #: f.widgets.cc:250 msgid "Upright a rotated image" msgstr "Orientar imagem rotacionada" #: f.widgets.cc:251 f.widgets.cc:252 msgid "Fast auto enhance that may work OK" msgstr "Realce automático rápido que deve funcionar bem" #: f.widgets.cc:253 msgid "Adjust brightness, contrast, color" msgstr "Ajusta brilho, contraste, cor" #: f.widgets.cc:254 msgid "Add local contrast, enhance details" msgstr "Adicionar contraste local, realçar detalhes" #: f.widgets.cc:255 msgid "Adjust brightness distribution" msgstr "Ajustar distribuição de brilho" #: f.widgets.cc:256 msgid "Flatten zonal brightness distribution" msgstr "Achatar distribuição de brilho zonal" #: f.widgets.cc:257 msgid "Change pixel dimensions" msgstr "Mudar dimensões de pixel" #: f.widgets.cc:258 msgid "Mirror image horizontally or vertically" msgstr "Espelhar imagem horizontalmente ou verticalmente" #: f.widgets.cc:259 msgid "Write text on image" msgstr "Escrever texto em imagem" #: f.widgets.cc:260 msgid "Write lines or arrows on image" msgstr "Gravar linhas ou setas na imagem" #: f.widgets.cc:261 msgid "Fix brightness uniformity across image" msgstr "Corrigir uniformidade de brilho por toda imagem" #: f.widgets.cc:262 msgid "Paint edit function gradually with mouse" msgstr "Função de pintura gradual com o mouse" #: f.widgets.cc:263 msgid "Leverage edits by brightness or color" msgstr "Influenciar edições por brilho ou cor" #: f.widgets.cc:264 msgid "Edit plugins menu or run a plugin function" msgstr "Editar menu de plugins ou executar uma função de plugin" #: f.widgets.cc:267 msgid "Make the image look sharper" msgstr "Tornar a imagem mais nítida" #: f.widgets.cc:268 msgid "Make the image look fuzzy" msgstr "Deixam a imagem difusa" #: f.widgets.cc:269 msgid "Filter noise from low-light photos" msgstr "Filtrar ruído de fotos com pouca luz" #: f.widgets.cc:270 msgid "Remove unwanted objects" msgstr "Remover objetos indesejados" #: f.widgets.cc:271 msgid "Fix red-eyes from electronic flash" msgstr "Corrigir olhos vermelhos por flash eletrônico" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Pintar pixels de imagem utilizando o mouse" #: f.widgets.cc:273 msgid "Paint image transparency using the mouse" msgstr "Aplicar transparência de imagem usando o mouse" #: f.widgets.cc:274 msgid "Make BW/color, negative/positive, sepia" msgstr "Fazer PB/colorido, negativo/positivo, sepia" #: f.widgets.cc:275 msgid "Shift/convert colors into other colors" msgstr "Deslocar/converter cores em outras" #: f.widgets.cc:276 msgid "Adjust color using RGB or CMY colors" msgstr "Ajustar cor usando cores RGB ou CMY" #: f.widgets.cc:277 msgid "Adjust color using HSL colors" msgstr "Ajustar cor usando cores HSL" #: f.widgets.cc:278 msgid "Adjust color in selected image areas" msgstr "Ajustar cor em áreas selecionadas da imagem" #: f.widgets.cc:279 msgid "Match colors on one image with another" msgstr "Equiparar cores de uma imagem com outra" #: f.widgets.cc:280 msgid "Convert to another color profile" msgstr "Converter para outro perfil de cores" #: f.widgets.cc:281 msgid "Remove dust spots from scanned slides" msgstr "Remover pontos de sujeira de digitalizações" #: f.widgets.cc:282 msgid "Smoothen edges with jaggies" msgstr "Suavisar contornos irregulares" #: f.widgets.cc:283 msgid "Reduce Chromatic Abberation" msgstr "Reduzir Aberrações Cromáticas" #: f.widgets.cc:284 msgid "Erase known hot and dark pixels" msgstr "Apagar pixels quentes ou escuros conhecidos" #: f.widgets.cc:287 msgid "Remove curvature, esp. panoramas" msgstr "Remover curvatura, esp. panoramas" #: f.widgets.cc:288 msgid "Straighten objects seen from an angle" msgstr "Endireitar objetos vistos a um ângulo" #: f.widgets.cc:289 msgid "Distort image areas using the mouse" msgstr "Distorcer as áreas da imagem usando o mouse" #: f.widgets.cc:290 f.widgets.cc:291 f.widgets.cc:292 msgid "Distort the whole image using the mouse" msgstr "Distorcer toda a imagem utilizando o mouse" #: f.widgets.cc:293 msgid "Flatten a photographed book page" msgstr "Achatar uma página fotografada de um book" #: f.widgets.cc:296 msgid "Reduce color depth (posterize)" msgstr "Reduzir profundidade de cor (posterizar)" #: f.widgets.cc:297 msgid "Convert to pencil sketch" msgstr "Converter para esboço a lápis" #: f.widgets.cc:298 msgid "Convert to line drawing (edge detection)" msgstr "Converter para desenho de contorno (detecção de borda)" #: f.widgets.cc:299 msgid "Convert to solid color drawing" msgstr "Converter em desenho de cor sólida" #: f.widgets.cc:300 msgid "Graduated Blur depending on contrast" msgstr "Desfoque Graduado dependente do contraste" #: f.widgets.cc:301 msgid "Create an embossed or 3D appearance" msgstr "Criar relevo ou aparência de 3D" #: f.widgets.cc:302 msgid "Convert to square tiles" msgstr "Converter para ladrilhos quadrados" #: f.widgets.cc:303 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "Converter para pontos (efeito Roy Lichtenstein)" #: f.widgets.cc:304 msgid "Convert into a simulated painting" msgstr "Converter em uma pintura simulada" #: f.widgets.cc:305 msgid "Change brightness or color radially" msgstr "Alterar o brilho ou cor radialmente" #: f.widgets.cc:306 msgid "Add texture to an image" msgstr "Adicionar textura a uma imagem" #: f.widgets.cc:307 msgid "Tile image with a repeating pattern" msgstr "Ladrilhar imagem com um padrão repetitivo" #: f.widgets.cc:308 msgid "Create a mosaic with tiles made from all images" msgstr "Criar um mosaico com ladrilhos feitos de todas as imagens" #: f.widgets.cc:309 msgid "Process an image using a custom kernel" msgstr "Processar uma imagem usando um núcleo customizado" #: f.widgets.cc:310 msgid "Warp an image with a wave pattern" msgstr "Distorce uma imagem com um padrão de onda" #: f.widgets.cc:311 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:312 msgid "Make a spherical projection of an image" msgstr "Criar uma projeção esférica de uma imagem" #: f.widgets.cc:315 msgid "Combine bright/dark images for better detail" msgstr "Combine imagens claras/escuras para melhores detalhes" #: f.widgets.cc:316 msgid "Combine near/far focus images for deeper focus" msgstr "Combinar imagens de foco perto/longe para foco profundo" #: f.widgets.cc:317 msgid "Combine images to erase passing people, etc." msgstr "Combinar imagens para apagar pessoas que passam, etc" #: f.widgets.cc:318 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:319 msgid "Combine images into a panorama" msgstr "Combinar imagens em um panorama" #: f.widgets.cc:320 msgid "Combine images into a vertical panorama" msgstr "Combinar imagens em um panorama vertical" #: f.widgets.cc:321 msgid "Combine images into a panorama (panorama tools)" msgstr "Combinar imagens em um panorama (panorama tools)" #: f.widgets.cc:322 msgid "Arrange images and text in a layout (montage)" msgstr "Arranjar imagens e texto em um leiaute (montagem)" #: f.widgets.cc:325 msgid "Index new files and make thumbnails" msgstr "Indexar novos arquivos gerar miniaturas" #: f.widgets.cc:326 msgid "Change user preferences" msgstr "Mudar preferências de usuário" #: f.widgets.cc:327 msgid "Change Keyboard Shortcut Keys" msgstr "Mudar Atalhos de Teclado" #: f.widgets.cc:328 msgid "Show a brightness distribution graph" msgstr "Mostrar gráfico de distribuição de brilho" #: f.widgets.cc:329 msgid "Show or revise grid lines" msgstr "Mostrar ou corrigir linhas de grade" #: f.widgets.cc:330 msgid "Change color of foreground lines" msgstr "Mudar cor de linhas de primeiro plano" #: f.widgets.cc:331 msgid "Show RGB colors at mouse click" msgstr "Mostrar cores RGB ao clique do mouse" #: f.widgets.cc:332 msgid "Magnify image around the mouse position" msgstr "Ampliar imagem em torno da posição do mouse" #: f.widgets.cc:333 msgid "Highlight darkest and brightest pixels" msgstr "Destacar pixels mais escuros e mais brilhantes" #: f.widgets.cc:334 msgid "Chart to adjust monitor color" msgstr "Gráfico para ajustar cores do monitor" #: f.widgets.cc:335 msgid "Chart to adjust monitor gamma" msgstr "Gráfico para ajustar gama do monitor" #: f.widgets.cc:336 msgid "Change the GUI language" msgstr "Mudar a língua da GUI" #: f.widgets.cc:337 msgid "Report missing translations" msgstr "Relatar tradução em falta" #: f.widgets.cc:338 msgid "Calibrate printer colors" msgstr "Calibrar cores de impressora" #: f.widgets.cc:339 msgid "Memory and CPU (to terminal/logfile)" msgstr "Memória e CPU (para terminal/arquivo log)" #: f.widgets.cc:343 msgid "Rename/convert/resize/move multiple files" msgstr "Renomear/converter/redimensionar/mover vários arquivos" #: f.widgets.cc:344 msgid "Upright multiple rotated image files" msgstr "Orientar múltiplos arquivos de imagem rotacionados" #: f.widgets.cc:345 msgid "Delete or Trash multiple files" msgstr "Excluir ou Remover vários arquivos" #: f.widgets.cc:346 msgid "Convert camera RAW files using DCraw" msgstr "Converter arquivos RAW da camera usando o DCRaw" #: f.widgets.cc:347 msgid "Convert camera RAW files using Raw Therapee" msgstr "Converter arquivos RAW da camera usando o Raw Therapee" #: f.widgets.cc:348 msgid "Build and run edit script files" msgstr "Criar, editar e executar arquivo de script" #: f.widgets.cc:349 msgid "Burn selected image files to CD or DVD" msgstr "Gravar imagens selecionadas em CD ou DVD" #: f.widgets.cc:350 msgid "Search all image files and report duplicates" msgstr "Procurar em todas os arquivos de imagem e relatar duplicidades" #: f.widgets.cc:354 msgid "Quick Start mini-guide" msgstr "Mini-guia resumido" #: f.widgets.cc:355 msgid "Read the user guide" msgstr "Leia o Guia de Usuários" #: f.widgets.cc:356 msgid "Recent user guide changes" msgstr "Mudanças recentes no Guia de Usuários" #: f.widgets.cc:357 msgid "Summary of image edit functions" msgstr "Resumo das funções de edição de imagem" #: f.widgets.cc:358 msgid "Technical installation notes" msgstr "Notas técnicas da instalação do Fotoxx" #: f.widgets.cc:359 msgid "List updates by Fotoxx version" msgstr "Listar atualizações por versão do Fotoxx" #: f.widgets.cc:360 msgid "View the log file and error messages" msgstr "Ver arquivo de log e mensagens de erro" #: f.widgets.cc:361 msgid "How to do Fotoxx translations" msgstr "Como fazer traduções para o Fotoxx" #: f.widgets.cc:362 msgid "Show the Fotoxx web page" msgstr "Mostrar a página do Fotoxx na web" #: f.widgets.cc:363 msgid "Version, license, contact, credits" msgstr "Versão, licença, contato, créditos" #: f.widgets.cc:366 msgid "Organize images into albums" msgstr "Organizar imagens em Álbuns" #: f.widgets.cc:367 msgid "Start a slide show" msgstr "Iniciar Apresentação de Slides" #: f.widgets.cc:368 msgid "Set desktop wallpaper from current Fotoxx image" msgstr "Configurar plano de fundo com a imagem atual no Fotoxx" #: f.widgets.cc:369 msgid "Cycle desktop wallpaper from a Fotoxx album" msgstr "Criar rodízio de imagens a partir de álbum do Fotoxx" #: f.widgets.cc:391 msgid "New Window" msgstr "Nova Janela" #: f.widgets.cc:392 msgid "Sync Gallery" msgstr "Sinc. Galeria" #: f.widgets.cc:393 msgid "Recently Seen Images" msgstr "Imagens vistas recentemente" #: f.widgets.cc:394 msgid "Newest Images" msgstr "Imagens mais Novas" #: f.widgets.cc:396 msgid "Open Previous File" msgstr "Abrir arquivo anterior" #: f.widgets.cc:397 msgid "Open RAW (ufraw)" msgstr "Abrir RAW (ufraw)" #: f.widgets.cc:398 msgid "Open RAW (Raw Therapee)" msgstr "Abrir RAW (Raw Therapee)" #: f.widgets.cc:399 msgid "New Blank Image" msgstr "Nova Imagem em Branco" #: f.widgets.cc:402 msgid "Delete Image File" msgstr "Excluir arquivo de Imagem" #: f.widgets.cc:403 msgid "Print Image" msgstr "Imprimir Imagem" #: f.widgets.cc:404 msgid "Print Calibrated Image" msgstr "Imprimir Imagem Calibrada" #: f.widgets.cc:405 f.widgets.cc:550 msgid "Set Desktop Wallpaper" msgstr "Configurar Plano de Fundo" #: f.widgets.cc:409 msgid "View Metadata (short)" msgstr "Visualizar Metadados (curto)" #: f.widgets.cc:410 msgid "View Metadata (long)" msgstr "Visualizar Metadados (longo)" #: f.widgets.cc:411 msgid "Show Captions on Image" msgstr "Mostrar Legendas na Imagem" #: f.widgets.cc:413 msgid "Edit Any Metadata" msgstr "Editar qualquer Metadado" #: f.widgets.cc:416 f.widgets.cc:541 msgid "Batch Rename Tags" msgstr "Renomear Etiquetas em Lote" #: f.widgets.cc:417 f.widgets.cc:542 msgid "Batch Add/Change Metadata" msgstr "Adicionar/Modificar Metadados em Lote" #: f.widgets.cc:420 f.widgets.cc:544 f.widgets.cc:618 msgid "Images by Geotag" msgstr "Imagens por Georreferência" #: f.widgets.cc:421 f.widgets.cc:545 f.widgets.cc:619 msgid "Search Images" msgstr "Procurar Imagens" #: f.widgets.cc:424 fotoxx.h:1168 msgid "Select" msgstr "Selecionar" #: f.widgets.cc:425 fotoxx.h:1170 msgid "Show" msgstr "Mostrar" #: f.widgets.cc:426 fotoxx.h:1124 msgid "Hide" msgstr "Ocultar" #: f.widgets.cc:427 fotoxx.h:1110 msgid "Enable" msgstr "Habilitar" #: f.widgets.cc:428 fotoxx.h:1106 msgid "Disable" msgstr "Desabilitar" #: f.widgets.cc:429 fotoxx.h:1127 msgid "Invert" msgstr "Inverter" #: f.widgets.cc:430 fotoxx.h:1184 msgid "Unselect" msgstr "Deselecionar" #: f.widgets.cc:431 msgid "Copy Area" msgstr "Copiar Área" #: f.widgets.cc:432 msgid "Paste Area" msgstr "Colar Área" #: f.widgets.cc:433 msgid "Open Area File" msgstr "Abrir Arquivo de Área" #: f.widgets.cc:434 msgid "Save Area File" msgstr "Salvar Arquivo de Área" #: f.widgets.cc:439 msgid "Voodoo 1" msgstr "Voodoo 1" #: f.widgets.cc:440 msgid "Voodoo 2" msgstr "Voodoo 2" #: f.widgets.cc:442 f.widgets.cc:687 msgid "Brightness Dist." msgstr "Dist. de Brilho" #: f.widgets.cc:443 f.widgets.cc:688 msgid "Zonal Flatten" msgstr "Achatamento Zonal" #: f.widgets.cc:447 msgid "Add Text" msgstr "Escrever Texto" #: f.widgets.cc:448 msgid "Add Lines" msgstr "Adicionar Linhas" #: f.widgets.cc:451 msgid "Plugins" msgstr "Extensões" #: f.widgets.cc:456 msgid "Denoise" msgstr "Reduzir ruído" #: f.widgets.cc:458 msgid "Red Eyes" msgstr "Olhos vermelhos" #: f.widgets.cc:463 msgid "Adjust RGB/CMY" msgstr "Ajustar RGB/CMY" #: f.widgets.cc:464 msgid "Adjust HSL" msgstr "Ajustar HSL" #: f.widgets.cc:465 msgid "Brightness Ramp" msgstr "Curvas de brilho" #: f.widgets.cc:467 msgid "Match Colors" msgstr "Mesclar Cores" #: f.widgets.cc:468 msgid "Color Profile" msgstr "Perfil de Cores" #: f.widgets.cc:470 msgid "Anti-Alias" msgstr "Antisserrilhado" #: f.widgets.cc:476 msgid "Fix Perspective" msgstr "Corrigdir perspectiva" #: f.widgets.cc:481 msgid "Flatten Book Page" msgstr "Achatar Página de Book" #: f.widgets.cc:484 msgid "Color Depth" msgstr "Resolução de cor" #: f.widgets.cc:485 msgid "Sketch" msgstr "Esboço" #: f.widgets.cc:489 msgid "Embossing" msgstr "Alto-relevo" #: f.widgets.cc:491 msgid "Dots" msgstr "Pontos" #: f.widgets.cc:492 msgid "Painting" msgstr "Pintura" #: f.widgets.cc:494 msgid "Texture" msgstr "Textura" #: f.widgets.cc:496 msgid "Mosaic" msgstr "Mosaico" #: f.widgets.cc:503 msgid "High Dynamic Range" msgstr "Grande Alcance Dinâmico (HDR)" #: f.widgets.cc:504 msgid "High Depth of Field" msgstr "Alta Profundidade de Campo (HDF)" #: f.widgets.cc:505 msgid "Stack/Paint" msgstr "Pilha/Pintura" #: f.widgets.cc:506 msgid "Stack/Noise" msgstr "Pilha/Ruído" #: f.widgets.cc:507 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:508 msgid "Vertical Panorama" msgstr "Panorama Vertical" #: f.widgets.cc:509 msgid "PT Panorama" msgstr "Panorama PT" #: f.widgets.cc:510 msgid "Mashup" msgstr "Mistura" #: f.widgets.cc:515 msgid "Keyboard Shortcuts" msgstr "Teclas de Atalho" #: f.widgets.cc:516 msgid "Show Brightness Dist." msgstr "Mostrar Dist. de Brilho" #: f.widgets.cc:521 msgid "Dark/Bright Pixels" msgstr "Pixels Claros/Escuros" #: f.widgets.cc:522 msgid "Monitor Color" msgstr "Cores do Monitor" #: f.widgets.cc:524 msgid "Change Language" msgstr "Trocar língua" #: f.widgets.cc:525 msgid "Missing Translations" msgstr "Traduções em falta" #: f.widgets.cc:527 msgid "Resources" msgstr "Recursos" #: f.widgets.cc:534 msgid "Batch RAW (DCraw)" msgstr "RAW em Lote (DCRaw)" #: f.widgets.cc:535 msgid "Batch RAW (Raw Therapee)" msgstr "RAW em Lote (Raw Therapee)" #: f.widgets.cc:536 msgid "Script Files" msgstr "Arquivos de Script" #: f.widgets.cc:537 msgid "Burn Images to CD/DVD" msgstr "Gravar imagens em CD/DVD" #: f.widgets.cc:551 msgid "Cycle Desktop Wallpaper" msgstr "Criar rodízio de planos de fundo" #: f.widgets.cc:568 f.widgets.cc:591 f.widgets.cc:613 msgid "Gallery" msgstr "Galeria" #: f.widgets.cc:569 f.widgets.cc:592 f.widgets.cc:614 msgid "World Maps" msgstr "Mapas Mundiais" #: f.widgets.cc:571 msgid "Favorites" msgstr "Favoritos" #: f.widgets.cc:573 fotoxx.h:1164 msgid "Save" msgstr "Salvar" #: f.widgets.cc:574 msgid "Prev/Next" msgstr "Ant./Prox." #: f.widgets.cc:575 msgid "Metadata" msgstr "Metadados" #: f.widgets.cc:576 msgid "Areas" msgstr "Áreas" #: f.widgets.cc:577 fotoxx.h:1109 msgid "Edit" msgstr "Editar" #: f.widgets.cc:578 msgid "Repair" msgstr "Reparo" #: f.widgets.cc:579 msgid "Bend" msgstr "Curvar" #: f.widgets.cc:580 msgid "Effects" msgstr "Efeitos" #: f.widgets.cc:581 msgid "Combine" msgstr "Combinar" #: f.widgets.cc:582 msgid "Undo/Redo" msgstr "Desfazer/Refazer" #: f.widgets.cc:583 msgid "Tools" msgstr "Ferramentas" #: f.widgets.cc:594 msgid "Sync.G" msgstr "Sinc.G" #: f.widgets.cc:595 msgid "Albums" msgstr "Álbuns" #: f.widgets.cc:596 msgid "Bookmarks" msgstr "Favoritos" #: f.widgets.cc:605 msgid "Batch" msgstr "Lote" #: f.widgets.cc:616 msgid "Choose Map" msgstr "Escolher Mapa" #: f.widgets.cc:617 msgid "Search Range" msgstr "Buscar Intervalo" #: f.widgets.cc:625 fotoxx.h:1182 msgid "Undo" msgstr "Desfazer" #: f.widgets.cc:626 fotoxx.h:1155 msgid "Redo" msgstr "Refazer" #: f.widgets.cc:667 msgid "Popup Image" msgstr "Imagem Popup" #: f.widgets.cc:668 msgid "Popup Image (add)" msgstr "Imagem Popup (adicionar)" #: f.widgets.cc:672 fotoxx.h:1158 msgid "Rename" msgstr "Renomear" #: f.widgets.cc:673 msgid "Copy to Location" msgstr "Copiar para Localidade" #: f.widgets.cc:674 msgid "Move to Location" msgstr "Mover para Localidade" #: f.widgets.cc:675 msgid "Copy to Clipboard" msgstr "Copiar para Área de Transferência" #: f.widgets.cc:676 msgid "Remove from Album" msgstr "Remover do Álbum" #: f.widgets.cc:677 msgid "Cut to Image Cache" msgstr "Recortar para Reserva de Imagens" #: f.widgets.cc:678 msgid "Copy to Image Cache" msgstr "Copiar para Reserva de Imagens" #: f.widgets.cc:679 msgid "Paste Image Cache Here (clear)" msgstr "Colar Reserva de Imagens aqui (limpar)" #: f.widgets.cc:680 msgid "Paste Image Cache Here (keep)" msgstr "Colar Reserva de Imagens aqui (manter)" #: f.widgets.cc:681 msgid "Paste Current Image File Here" msgstr "Colar Arquivo de Imagem Atual Aqui" #: f.widgets.cc:685 msgid "Voodoo Enhance" msgstr "Realce Vudu" #: f.widgets.cc:690 msgid "Select Area" msgstr "Selecionar Área" #: f.widgets.cc:693 fotoxx.h:1178 msgid "Trash" msgstr "Lixeira" #: f.widgets.cc:819 msgid "Image Locations" msgstr "Localidade de Imagem" #: fotoxx-15.11.cc:373 msgid "Please install missing programs:" msgstr "Por favor, instale os programas que faltam:" #: fotoxx-15.11.cc:662 msgid "Kill active dialog?" msgstr "Fechar caixa de diálogo ativa?" #: fotoxx-15.11.cc:733 msgid "(reduced)" msgstr "(reduzido)" #: fotoxx-15.11.cc:734 msgid "area active" msgstr "área ativada" #: fotoxx-15.11.cc:735 msgid "dialog open" msgstr "caixa de diálogo aberta" #: fotoxx-15.11.cc:736 msgid "blocked" msgstr "bloqueado" #: fotoxx-15.11.cc:786 msgid "edits" msgstr "edições" #: fotoxx-15.11.cc:2793 msgid "Exceed 50 anchor points" msgstr "Excedeu 50 pontos de ancoragem" #: fotoxx-15.11.cc:3013 msgid "load curve from a file" msgstr "carregar curva de arquivo" #: fotoxx-15.11.cc:3079 msgid "curve file is invalid" msgstr "arquivo de curva inválido" #: fotoxx-15.11.cc:3093 msgid "save curve to a file" msgstr "salvar curva para arquivo" #: fotoxx-15.11.cc:3165 msgid "Too many edits, please save image" msgstr "Muitas edições, favor salvar imagem" #: fotoxx-15.11.cc:3170 msgid "this function cannot be scripted" msgstr "esta função não pode ser automatizada" #: fotoxx-15.11.cc:3187 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Área selecionada não ativa.\n" "Continuar?" #: fotoxx-15.11.cc:3499 msgid "file data does not fit dialog" msgstr "dados do arquivo não cabem em caixa de diálogo" #: fotoxx-15.11.cc:3509 msgid "Save settings to a file" msgstr "Salvar configurações em arquivo" #: fotoxx-15.11.cc:3825 msgid "This action will discard changes to current image" msgstr "Esta ação irá descartar as alterações para a imagem atual" #: fotoxx-15.11.cc:3826 msgid "prior function still active" msgstr "função prévia ainda ativa" #: fotoxx-15.11.cc:3827 fotoxx.h:1128 msgid "Keep" msgstr "Manter" #: fotoxx-15.11.cc:3828 msgid "Discard" msgstr "Descartar" #: fotoxx.h:1080 msgid "Add" msgstr "Adicionar" #: fotoxx.h:1081 msgid "Add All" msgstr "Adicionar todos" #: fotoxx.h:1083 msgid "Amount" msgstr "Quantidade" #: fotoxx.h:1084 msgid "Angle" msgstr "Ângulo" #: fotoxx.h:1085 msgid "Apply" msgstr "Aplicar" #: fotoxx.h:1086 msgid "Auto" msgstr "Auto" #: fotoxx.h:1087 msgid "Black" msgstr "Preto" #: fotoxx.h:1088 msgid "Blend Width" msgstr "Quantidade de mistura" #: fotoxx.h:1090 zfuncs.cc:10098 msgid "bottom" msgstr "rodapé" #: fotoxx.h:1092 msgid "Browse" msgstr "Procurar" #: fotoxx.h:1093 msgid "Cancel" msgstr "Cancelar" #: fotoxx.h:1094 msgid "Center" msgstr "centralizar" #: fotoxx.h:1095 msgid "Choose" msgstr "Escolher" #: fotoxx.h:1096 msgid "Clear" msgstr "Limpar" #: fotoxx.h:1097 msgid "Color" msgstr "Cor" #: fotoxx.h:1098 msgid "continue" msgstr "continuar" #: fotoxx.h:1100 msgid "Copy" msgstr "Copiar" #: fotoxx.h:1101 msgid "Create" msgstr "Criar" #: fotoxx.h:1102 msgid "Curve File:" msgstr "Arquivo de curva:" #: fotoxx.h:1103 msgid "Cut" msgstr "Recortar" #: fotoxx.h:1104 msgid "Deband" msgstr "Debandar" #: fotoxx.h:1105 msgid "Delete" msgstr "Exlcuir" #: fotoxx.h:1107 msgid "Done" msgstr "Pronto" #: fotoxx.h:1108 msgid "edge" msgstr "borda" #: fotoxx.h:1111 msgid "Erase" msgstr "Apagar" #: fotoxx.h:1112 msgid "Fetch" msgstr "Buscar" #: fotoxx.h:1113 msgid "output file already exists" msgstr "arquivo de saída já existe" #: fotoxx.h:1114 #, c-format msgid "%d files selected" msgstr "%d arquivos selecionados" #: fotoxx.h:1115 msgid "Find" msgstr "Encontrar" #: fotoxx.h:1116 msgid "Finish" msgstr "Finalizar" #: fotoxx.h:1117 msgid "Flatten" msgstr "Abrandar" #: fotoxx.h:1118 msgid "Font" msgstr "Fonte" #: fotoxx.h:1119 msgid "Geotags" msgstr "Georreferências" #: fotoxx.h:1121 msgid "Grid" msgstr "Grade" #: fotoxx.h:1122 msgid "Height" msgstr "Altura" #: fotoxx.h:1125 msgid "Images" msgstr "Imagens" #: fotoxx.h:1126 msgid "Insert" msgstr "Inserir" #: fotoxx.h:1129 zfuncs.cc:10102 msgid "left" msgstr "esquerda" #: fotoxx.h:1130 msgid "limit" msgstr "limite" #: fotoxx.h:1131 msgid "Load" msgstr "Carga" #: fotoxx.h:1132 msgid "Make" msgstr "Gerar" #: fotoxx.h:1134 msgid "Map" msgstr "Mapa" #: fotoxx.h:1135 msgid "Max" msgstr "Máximo" #: fotoxx.h:1136 msgid "Negative" msgstr "Negativo" #: fotoxx.h:1137 msgid "New" msgstr "Novo" #: fotoxx.h:1138 msgid "Next" msgstr "Próximo" #: fotoxx.h:1139 zfuncs.cc:9190 msgid "No" msgstr "Não" #: fotoxx.h:1140 msgid "no images" msgstr "não há imagens" #: fotoxx.h:1142 msgid "no selection" msgstr "não há seleção" #: fotoxx.h:1143 msgid "OK" msgstr "OK" #: fotoxx.h:1145 msgid "Paste" msgstr "Colar" #: fotoxx.h:1146 msgid "Pause" msgstr "Pausar" #: fotoxx.h:1147 msgid "Percent" msgstr "Percentagem" #: fotoxx.h:1149 msgid "Presets" msgstr "Predefinições" #: fotoxx.h:1150 msgid "Prev" msgstr "Anterior" #: fotoxx.h:1151 msgid "Proceed" msgstr "Continuar" #: fotoxx.h:1153 msgid "range" msgstr "variação" #: fotoxx.h:1156 msgid "Reduce" msgstr "Reduzir" #: fotoxx.h:1157 msgid "Remove" msgstr "Remover" #: fotoxx.h:1159 msgid "Replace" msgstr "Substituir" #: fotoxx.h:1160 msgid "Reserved" msgstr "Reservados" #: fotoxx.h:1161 msgid "Reset" msgstr "Redefinir" #: fotoxx.h:1162 zfuncs.cc:10106 msgid "right" msgstr "direita" #: fotoxx.h:1163 msgid "Rotate" msgstr "Rotacionar" #: fotoxx.h:1165 msgid "Unknown file type, save as tiff/jpeg/png to edit" msgstr "Tipo de arquivo desconhecido, Salvar como tiff/jpeg/png para editar" #: fotoxx.h:1166 msgid "Search" msgstr "Buscar" #: fotoxx.h:1167 msgid "Seconds" msgstr "Segundos" #: fotoxx.h:1171 msgid "Size" msgstr "Tamanho" #: fotoxx.h:1172 msgid "Start" msgstr "Iniciar" #: fotoxx.h:1173 msgid "Strength" msgstr "Força" #: fotoxx.h:1174 msgid "Threshold" msgstr "Limiar" #: fotoxx.h:1175 #, c-format msgid "exceed %d files" msgstr "excedido %d arquivos" #: fotoxx.h:1176 zfuncs.cc:10094 msgid "top" msgstr "topo" #: fotoxx.h:1177 msgid "Transparency" msgstr "Transparência" #: fotoxx.h:1179 msgid "Trim" msgstr "Recortar" #: fotoxx.h:1180 msgid "Undo All" msgstr "Desfazer tudo" #: fotoxx.h:1181 msgid "Undo Last" msgstr "Desfazer último" #: fotoxx.h:1183 msgid "Unfinish" msgstr "Inacabado" #: fotoxx.h:1185 msgid "View" msgstr "Ver" #: fotoxx.h:1186 msgid "Web" msgstr "Web" #: fotoxx.h:1187 msgid "White" msgstr "Branco" #: fotoxx.h:1188 msgid "Width" msgstr "Largura" #: fotoxx.h:1189 msgid "x-offset" msgstr "deslocamento-x" #: fotoxx.h:1190 msgid "y-offset" msgstr "deslocamento-y" #: fotoxx.h:1191 zfuncs.cc:9190 msgid "Yes" msgstr "Sim" #: zfuncs.cc:1601 #, c-format msgid "" "create directory? \n" " %s" msgstr "" "criar diretório? \n" " %s" #: zfuncs.cc:4378 msgid "user guide not found" msgstr "guia de usuário não encontrado" #: zfuncs.cc:5196 #, c-format msgid "cannot open file %s" msgstr "não foi possível abrir arquivo %s" #: zfuncs.cc:5232 msgid "save screen to file" msgstr "salvar tela para arquivo" #: zfuncs.cc:9266 zfuncs.cc:9761 zfuncs.cc:10081 msgid "cancel" msgstr "cancelar" #: zfuncs.cc:9722 msgid "choose file" msgstr "escolher arquivo" #: zfuncs.cc:9727 msgid "choose files" msgstr "escolher arquivos" #: zfuncs.cc:9732 msgid "save" msgstr "salvar" #: zfuncs.cc:9738 msgid "choose folder" msgstr "escolher diretório" #: zfuncs.cc:9743 msgid "choose folders" msgstr "escolher diretórios" #: zfuncs.cc:9748 msgid "create folder" msgstr "criar pasta" #: zfuncs.cc:9755 msgid "hidden" msgstr "oculto" #: zfuncs.cc:10081 msgid "done" msgstr "pronto" #: zfuncs.cc:10081 zfuncs.cc:10090 msgid "margins" msgstr "margens" #: zfuncs.cc:10111 msgid "image scale" msgstr "escala de imagem" #: zfuncs.cc:10113 msgid "percent" msgstr "porcentagem" #: zfuncs.cc:10120 msgid "image" msgstr "imagem" #: zfuncs.cc:10124 msgid "width" msgstr "largura" #: zfuncs.cc:10128 msgid "height" msgstr "altura" #~ msgid "Lock aspect ratio" #~ msgstr "Manter proporção" #~ msgid "unsharp mask" #~ msgstr "desaguçar máscara" #~ msgid "brightness gradient" #~ msgstr "gradiente de brilho" #~ msgid "Kuwahara method" #~ msgstr "Método Kuwahara" #~ msgid "Flatten Outliers 1" #~ msgstr "Achatamento Atípico 1" #~ msgid "Flatten Outliers 2" #~ msgstr "Achatamento Atípico 2" #~ msgid "Median Brightness" #~ msgstr "Brilho Mediano" #~ msgid "" #~ "Set the file name and location \n" #~ "for the output color map file." #~ msgstr "" #~ "Configurar o nome e localização do arquivo \n" #~ "de saída para o mapa de cores." #~ msgid "Adjust Brightness Dist." #~ msgstr "Ajustar Dist. de Brilho" #~ msgid "" #~ "new name/base/adder unreasonable\n" #~ " e.g. newname ### 100 10" #~ msgstr "" #~ "novo nome/base/somedor anormal \n" #~ " ex. novo-nome ### 100 10" #~ msgid "Cycle Desktop Image" #~ msgstr "Rodízio de Imagem da Área de Trabalho" #~ msgid "Set Desktop Image" #~ msgstr "Definir Imagem da Área de Trabalho" #~ msgid "Cycle desktop images from a Fotoxx album" #~ msgstr "Criar rodízio de imagens através de um álbum do Fotoxx" #~ msgid "Set desktop image from current Fotoxx image" #~ msgstr "Definir imagem de Área de Trabalho através de imagem Fotoxx aberta" #~ msgid "Albums: Manage Albums, Slide Show, Desktop Background" #~ msgstr "Álbuns: Gerenciar álbuns, Apresentação de Slides, Papel de Parede" #~ msgid "jpeg quality must be 1-100" #~ msgstr "qualidade de jpeg deve estar entre 1-100" #~ msgid "(%d images)" #~ msgstr "(%d imagens)" #~ msgid "(0 images)" #~ msgstr "(0 imagens)" fotoxx-15.11.1/f.file.old.cc0000664000175000017500000042236012616075370014116 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** Fotoxx image edit - file menu m_clone start a new parallel instance of Fotoxx m_open open image file menu function m_open_drag open drag/drop image file m_previous open the previous image file add_recent_file add an image file to the list of recent files m_recentfiles show gallery of recently viewed files m_newfiles show gallery of newest image files m_prev open previous file in current gallery m_next open next file in current gallery m_prev_next open previous or next file in current gallery m_ufraw open a camera RAW file using the Ufraw program m_rawtherapee open a camera RAW file using the RawTherapee program m_file_save save a (modified) image file to disk file_new_version get next avail. file version name m_rename rename current image file or clicked thumbnail m_create create a new monocolor image create_blank_file callable function to create a new monocolor image m_copytoloc copy an image file to a new location (duplicate) m_movetoloc move an image file to a new location copy_move callable function to copy or move an image file m_trash move an image file to trash location m_delete delete an image file m_print print an image file m_copyto_clip copy clicked file or current file to the clipboard m_quit menu quit quitxx callable quit m_help help menu f_preload preload image files ahead of need f_open open and display an image file f_open_saved open the last image file saved 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 find_imagefiles find all image files under a given directory path file_copytoclipboard copy image file to GTK clipboard 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) ***************************************************************************/ #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; } /**************************************************************************/ // open file menu function void m_open(GtkWidget *, cchar *) { F1_help_topic = "open_image_file"; if (! CEF && checkpend("all")) return; // 15.04 f_open(null); return; } /**************************************************************************/ // open drag-drop file void m_open_drag(int x, int y, char *file) { F1_help_topic = "open_image_file"; if (! CEF && checkpend("all")) return; // 15.04 f_open(file); return; } /**************************************************************************/ // open the previous file opened (not the same as toolbar [prev] button) // repeated use will cycle back and forth between two most recent files void m_previous(GtkWidget *, cchar *menu) { FILE *fid; char *file, buff[XFCC]; int Nth = 0, err; float gzoom; F1_help_topic = "open_previous_file"; if (! CEF && checkpend("all")) return; // 15.04 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; } /**************************************************************************/ // add a new file to the list of recent files, first position void add_recent_file(cchar *newfile) { FILE *fidr, *fidw; char *pp, tempfile[200], buff[XFCC]; int ii, err; strcpy(tempfile,recentfiles_file); strcat(tempfile,"_temp"); fidw = fopen(tempfile,"w"); // open output temp file if (! fidw) { zmessageACK(Mwin,"file error: %s",strerror(errno)); return; } fprintf(fidw,"%s\n",newfile); // add new file as first in list fidr = fopen(recentfiles_file,"r"); if (fidr) { for (ii = 0; ii < 100; ii++) { // read list of recent files <= 100 pp = fgets_trim(buff,XFCC,fidr,1); if (! pp) break; if (strmatch(pp,newfile)) continue; // skip new file if present if (*pp == '/') fprintf(fidw,"%s\n",pp); // write to output file } fclose(fidr); } fclose(fidw); err = rename(tempfile,recentfiles_file); if (err) zmessageACK(Mwin,"file error: %s",strerror(errno)); 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 = 5; // gallery type = recent files gallery(recentfiles_file,"initF"); // generate gallery of recent files m_viewmode(0,"G"); gallery(0,"paint",0); 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) // 15.05 { 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, cc, err, sort, Nxrec, ftf; sxrec_t sxrec; FILE *fid; NFxrec_t *NFxrec = 0; cc = maximages * sizeof(NFxrec_t); // allocate memory NFxrec = (NFxrec_t *) zmalloc(cc); Nxrec = 0; ftf = 1; 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); while (true) { err = read_sxrec_seq(sxrec,ftf); // read image index recs. if (err) break; ii = Nxrec; if (sort == 1) { // 15.07 if (strmatch(sxrec.pdate,"null")) continue; // use EXIF photo date strncpy0(NFxrec[ii].date,sxrec.pdate,16); // ignore images without photo date } else strncpy0(NFxrec[ii].date,sxrec.fdate,16); // else use file mod date NFxrec[ii].file = sxrec.file; // image file zfree(sxrec.tags); // free unused stuff zfree(sxrec.capt); zfree(sxrec.comms); zfree(sxrec.gtags); if (++Nxrec == maximages) { zmessageACK(Mwin,"too many image files"); goto cleanup; } } 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: for (ii = 0; ii < Nxrec; ii++) // free memory zfree(NFxrec[ii].file); zfree(NFxrec); free_resources(); navi::gallerytype = 6; // newest files gallery(searchresults_file,"initF"); // generate gallery of matching files m_viewmode(0,"G"); gallery(0,"paint",0); 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 previous or next file in current gallery list // index is -1 or +1 void x_prev_next(int index) { char *pfile; int err; static int busy = 0; F1_help_topic = "prev_next"; if (busy) return; // avoid "function busy" 15.05 if (! CEF && checkpend("all")) return; // CEF allowed if no mods 15.04 busy = 1; pfile = gallery_getnext(index,Flastversion); // get prev/next file (last version) if (! pfile) { Fpaint2(); zmessage_post(Mwin,2,ZTX("no more images")); // 15.08 goto returnx; } err = f_open(pfile,0,0,1,0); // open image or RAW file zfree(pfile); if (err) goto returnx; f_preload(index); // preload next image returnx: busy = 0; // 15.05 return; } void m_prev(GtkWidget *, cchar *menu) { if (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) { if (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; } /**************************************************************************/ // menu function: open a camera RAW file and edit with the ufraw GUI // opens 'clicked_file' if present or 'rawfile' if not void m_ufraw(GtkWidget *, cchar *menu) { char *pp; char *tiffile; int err; STATB statb; cchar *ufrawcommand = "ufraw --exposure=auto --interpolation=bilinear" " --out-type=tiff --out-depth=16 --overwrite" " --output=\"%s\" \"%s\""; F1_help_topic = "open_raw_file"; if (! Fufraw) { zmessageACK(Mwin,ZTX("UFraw not installed")); return; } if (! CEF && checkpend("all")) return; if (rawfile) zfree(rawfile); if (clicked_file) { rawfile = clicked_file; clicked_file = 0; } if (! rawfile) { pp = curr_file; // directory for file open dialog if (! pp) pp = curr_dirk; rawfile = zgetfile(ZTX("Open RAW file (ufraw)"),MWIN,"file",pp); // dialog to get RAW filespec if (! rawfile) return; } if (image_file_type(rawfile) != 3) { zmessageACK(Mwin,ZTX("RAW type not registered in User Settings")); zfree(rawfile); rawfile = 0; return; } zmainloop(); tiffile = raw_to_tiff(rawfile); shell_quiet(ufrawcommand,tiffile,rawfile); // start ufraw command, convert to tiff zfree(rawfile); rawfile = 0; err = stat(tiffile,&statb); if (err) { zfree(tiffile); printz("ufraw produced no tif-16 file\n"); return; } err = f_open(tiffile,0,0,1); // open tiff file if (err) { zfree(tiffile); return; } update_image_index(tiffile); // update index rec. 15.03 zfree(tiffile); 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, *temp; int err; STATB statb; cchar *command = "rawtherapee -o \"%s\" -t -Y \"%s\" "; F1_help_topic = "open_raw_file"; if (! Frawtherapee) { zmessageACK(Mwin,ZTX("Raw Therapee not installed")); return; } if (! CEF && checkpend("all")) return; if (rawfile) zfree(rawfile); if (clicked_file) { rawfile = clicked_file; clicked_file = 0; } if (! rawfile) { pp = curr_file; // directory for file open dialog if (! pp) pp = curr_dirk; rawfile = zgetfile(ZTX("Open RAW file (Raw Therapee)"),MWIN,"file",pp); if (! rawfile) return; } if (image_file_type(rawfile) != 3) { zmessageACK(Mwin,ZTX("RAW type not registered in User Settings")); zfree(rawfile); rawfile = 0; return; } zmainloop(); tiffile = raw_to_tiff(rawfile); shell_quiet(command,tiffile,rawfile); // start command, convert to tiff zfree(rawfile); rawfile = 0; temp = zstrdup(tiffile); // Raw Therapee ignores specified .tif pp = strstr(temp,".tif"); // and outputs .tif instead pp[4] = 0; rename(temp,tiffile); zfree(temp); err = stat(tiffile,&statb); if (err) { zfree(tiffile); printz("Raw Therapee produced no tif-16 file\n"); return; } err = f_open(tiffile,0,0,1); // open tiff file if (err) { zfree(tiffile); return; } update_image_index(tiffile); // update index rec. 15.03 zfree(tiffile); return; } /**************************************************************************/ // 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 | | | | [version] save as new file version | | [new file] save as new file name or type | | [replace] 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,"image","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; int err; char event2[12]; if (strmatch(event,"escape")) zd->zstat = 1; // escape = cancel 15.07 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 (strmatch(event2,"newvers")) { 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); } 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; } /**************************************************************************/ // 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 *newversion, *pfile, *pext, *pvers; cchar *delim; int err, nvers, ii; STATB fstat; newversion = zstrdup(file,12); pfile = strrchr(newversion,'/'); // get filename if (! pfile) return 0; pext = strrchr(pfile,'.'); // get .ext if (! pext) return 0; if (strlen(pext) > 5) return 0; // .jpeg = 5 chars. pvers = pext - 4; // get curr. version: filename.vNN.ext if (! strmatchN(pvers,".v",2)) nvers = 0; else { err = convSI(pvers+2,nvers,1,98,&delim); // convert NN to number 1-98 if (err > 1) return 0; // conversion error if (delim != pext) nvers = 0; // check format is .vNN } if (nvers == 0) { // no version in file name pvers = pext; pext += 4; memmove(pext,pvers,6); // make space for .vNN before .ext strncpy(pvers,".vNN",4); } for (ii = 99; ii > nvers; ii--) // look for higher file versions { pvers[2] = ii/10 + '0'; // build filename.vNN.ext pvers[3] = ii - 10 * (ii/10) + '0'; err = stat(newversion,&fstat); if (! err) break; } if (ii == 99) return 0; // 99th version found ii++; // next version nvers = ii; pvers[2] = ii/10 + '0'; // build filename.vNN.ext pvers[3] = ii - 10 * (ii/10) + '0'; return newversion; } /**************************************************************************/ // 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; if (menu) F1_help_topic = "rename_file"; if (checkpend("all")) return; 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; /*** ______________________________________ | Rename Image File | | | | Old Name [_______________________] | | New Name [_______________________] | | [previous name] [add 1] | | | | [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,"entry","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_run(zd,rename_dialog_event); // 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) gtk_window_present(MWIN); // keep focus on main window 15.03 return; } // dialog event and completion callback function int rename_dialog_event(zdialog *zd, cchar *event) { char *pp, *pdir, *pfile, *pext; char *newfile, *thumbfile, *nextfile; int nseq, digits, ccp, ccn, ccx, err, fnew; STATB statb; if (strmatch(event,"enter")) zd->zstat = 1; // [apply] if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"Bprev")) // previous name >> new name if (strlen(rename_prev) > 0) zdialog_stuff(zd,"newname",rename_prev); if (strmatch(event,"Badd1")) // increment sequence number { zdialog_fetch(zd,"newname",rename_new,194); // get entered filename pp = rename_new + strlen(rename_new); digits = 0; while (pp[-1] >= '0' && pp[-1] <= '9') { pp--; // look for NNN in filenameNNN digits++; } nseq = 1 + atoi(pp); // NNN + 1 if (nseq > 9999) nseq = 0; if (digits < 2) digits = 2; // keep digit count if enough if (nseq > 99 && digits < 3) digits = 3; // use leading zeros if (nseq > 999 && digits < 4) digits = 4; snprintf(pp,digits+1,"%0*d",digits,nseq); zdialog_stuff(zd,"newname",rename_new); } if (zd->zstat == 0) return 1; // not finished if (zd->zstat != 1) { // canceled zdialog_free(zd); // kill dialog zdrename = 0; if (rename_file) zfree(rename_file); rename_file = 0; return 1; } zd->zstat = 0; // apply - keep dialog active if (! rename_file) 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); return 1; } if (FGW == 'F') nextfile = gallery(0,"find",curr_file_posn+1); // save next file, before rename 15.03 else nextfile = 0; if (checkpend("all")) return 1; // commit change 15.04 err = rename(rename_file,newfile); if (err) { zmessageACK(Mwin,"file error: %s",strerror(errno)); zdialog_free(zd); zdrename = 0; zfree(newfile); zfree(rename_file); rename_file = 0; return 1; } thumbfile = image_thumbfile(newfile,&fnew); // stop use of leftover thumbnail if (fnew != 1) remove(thumbfile); zfree(thumbfile); strncpy0(rename_prev,rename_new,199); // save new name to previous name load_filemeta(newfile); // add new file to image index update_image_index(newfile); delete_image_index(rename_file); // delete old file in image index add_recent_file(newfile); // first in recent files list if (navi::gallerytype == 1) { // refresh gallery list 15.03 gallery(0,"init"); gallery(0,"paint",-1); } if (FGW == 'F') { if (nextfile) { // F view: open next 15.03 f_open(nextfile); zfree(nextfile); } else f_open(newfile); // no next, open renamed file 15.09 } zfree(newfile); zfree(rename_file); rename_file = 0; return 1; } /**************************************************************************/ // create a new blank image with desired background color void m_create(GtkWidget *, cchar *pname) { int create_dialog_event(zdialog *zd, cchar *event); 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,"entry","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,"spin","width","hbz","100|20000|1|1600"); zdialog_add_widget(zd,"label","space","hbz",0,"space=5"); zdialog_add_widget(zd,"label","labh","hbz",Bheight,"space=5"); zdialog_add_widget(zd,"spin","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_cb_app(zd,"ext",".bmp"); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_stuff(zd,"file",""); // force input of new name zdialog_run(zd,create_dialog_event); // 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")); // 15.03 zdialog_free(zd); Fblock = 0; m_create(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; } // dialog event and completion function int create_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 return 1; } // function to create a new blank image file // file extension must be one of: .jpg .tif .png .bmp // 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 if (strmatch(pp,".bmp")) fext = "bmp"; else return 2; tempxb = PXB_make(ww,hh,0); // create pixbuf image if (! tempxb) zappcrash("out of memory"); 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; } /**************************************************************************/ // right-click dropdown menu functions // copy an image file to a new location (duplicate) void m_copytoloc(GtkWidget *, cchar *) { copy_move("copy"); return; } // move an image file to a new location (delete original) void m_movetoloc(GtkWidget *, cchar *) { copy_move("move"); return; } // copy an image to a new location and optionally delete the original char *copymove_loc = 0; char *copymove_file = 0; int copymove_Fdelete; void copy_move(cchar *menu) { int copymove_dialog_event(zdialog *zd, cchar *event); cchar *copytitle = ZTX("Copy Image File"); cchar *movetitle = ZTX("Move Image File"); static char menux[8]; if (menu) strcpy(menux,menu); // remember for re-entry with null menu F1_help_topic = "copy_move_image"; if (checkpend("all")) return; 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; /*** XXXX Image File Image File: [________________] new location [______________] [browse] [apply] [cancel] ***/ if (! zdcopymove) { if (strmatch(menux,"copy")) zdcopymove = zdialog_new(copytitle,Mwin,Bapply,Bcancel,null); else zdcopymove = zdialog_new(movetitle,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,"entry","newloc","hb2",0,"expand"); zdialog_add_widget(zd,"button","browse","hb2",Bbrowse,"space=5"); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_resize(zd,400,0); zdialog_run(zd,copymove_dialog_event); // run dialog } zdialog *zd = zdcopymove; if (strmatch(menux,"copy")) zdialog_set_title(zd,copytitle); else if (strmatch(menux,"move")) zdialog_set_title(zd,movetitle); char *pfile = strrchr(copymove_file,'/'); if (pfile) pfile++; else pfile = copymove_file; zdialog_stuff(zd,"file",pfile); if (copymove_loc) zdialog_stuff(zd,"newloc",copymove_loc); if (strmatch(menux,"copy")) copymove_Fdelete = 0; // copy, no delete else copymove_Fdelete = 1; // move, delete original if (FGW == 'F') gtk_window_present(MWIN); // keep focus on main window 15.03 return; } // dialog event and completion callback function int copymove_dialog_event(zdialog *zd, cchar *event) { char *newloc, *pfile, *newfile = 0, *nextfile = 0; int cc, err, Nth; STATB statb; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"browse")) { // browse for new location if (! copymove_loc) { copymove_loc = (char *) zmalloc(XFCC); // 15.08 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 (zd->zstat == 0) return 1; // wait for dialog finished if (zd->zstat != 1) { // canceled zdialog_free(zd); // kill dialog zdcopymove = 0; return 1; } zd->zstat = 0; // apply - keep dialog active if (! copymove_file) 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; } checkpend("all"); // commit change 15.04 err = shell_ack("cp -p \"%s\" \"%s\"",copymove_file,newfile); // copy source file to new file if (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 (FGW == 'F') { f_open(copymove_file); Nth = curr_file_posn + 1; nextfile = gallery(0,"find",Nth); } if (copymove_Fdelete) { // move - delete source file err = remove(copymove_file); if (err) { zmessageACK(Mwin,ZTX("delete failed: \n %s"),wstrerror(err)); zdialog_free(zd); zdcopymove = 0; zfree(newfile); return 1; } delete_image_index(copymove_file); // delete in image index } if (FGW == 'F') { // F view 15.03 if (nextfile) f_open(nextfile); // open next file else { // end of gallery zdialog_free(zd); // kill dialog zdcopymove = 0; if (copymove_Fdelete) free_resources(); // no curr. file } } if (navi::gallerytype == 1) { // refresh gallery 15.03 gallery(0,"init"); gallery(0,"paint",-1); } if (nextfile) zfree(nextfile); if (newfile) zfree(newfile); return 1; } /**************************************************************************/ // Trash image file - move curr_file to trash. // Use the Linux standard trash function. // If not available, revert to Desktop folder. char *trash_file = 0; void m_trash(GtkWidget *, cchar *) { int trash_dialog_event(zdialog *zd, cchar *event); cchar *title = ZTX("Trash Image File"); cchar *autostep = ZTX("(automatic step to next image)"); F1_help_topic = "trash_image"; if (checkpend("all")) return; if (trash_file) zfree(trash_file); trash_file = 0; if (clicked_file) { // use clicked file if present trash_file = clicked_file; clicked_file = 0; } else if (curr_file) // else use current file trash_file = zstrdup(curr_file); else return; /*** ______________________________________ | Trash Image File | | | | Image File: [______________________] | | | | (automatic step to next image) | | | | [trash] [keep] [cancel] | |______________________________________| ***/ if (! zdtrash) // start dialog if not already { zdtrash = zdialog_new(title,Mwin,Btrash,Bkeep,Bcancel,null); zdialog *zd = zdtrash; 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"); if (FGW == 'F') zdialog_add_widget(zd,"label","labnext","dialog",autostep); zdialog_resize(zd,300,0); zdialog_run(zd,trash_dialog_event); // run dialog } char *pfile = strrchr(trash_file,'/'); if (pfile) pfile++; else pfile = trash_file; zdialog_stuff(zdtrash,"file",pfile); gtk_window_present(MWIN); // keep focus on main window 15.03 return; } // dialog event and completion callback function int trash_dialog_event(zdialog *zd, cchar *event) { int err = 0, yn, nn, Nth, gstat; char *file, dtdirk[200], trashdir[200]; GError *gerror = 0; GFile *gfile = 0; STATB statb; static int trashworks = 1, desktopworks = 1; // assume OK until found otherwise FILE *fid; if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 cchar *mess1 = ZTX("Linux standard trash does not work. \n" "Desktop trash folder will be created."); cchar *mess2 = ZTX("Linux and Desktop trash do not work. \n" "Permanently delete the image file?"); cchar *mess3 = ZTX("Cannot create trash folder: %s"); if (zd->zstat == 0) return 1; // wait for dialog finished if (zd->zstat == 2) goto NEXT; // [keep] button if (zd->zstat != 1) goto KILL; // [cancel] or [x] if (! trash_file) goto KILL; // [trash] button err = stat(trash_file,&statb); // get file status if (err) goto KILL; if (! S_ISREG(statb.st_mode)) goto KILL; if (! (statb.st_mode & S_IWUSR)) { // check permission yn = zmessageYN(Mwin,ZTX("Move read-only file to trash?")); if (! yn) goto NEXT; // keep file statb.st_mode |= S_IWUSR; // make writable if needed chmod(trash_file,statb.st_mode); } if (checkpend("all")) goto KILL; if (trashworks) // try Linux standard trash { gfile = g_file_new_for_path(trash_file); gstat = g_file_trash(gfile,0,&gerror); // move file to trash g_object_unref(gfile); if (! gstat) { printz("standard trash failed: %s \n",gerror->message); // did not work zmessageACK(Mwin,mess1); trashworks = 0; } } if (! trashworks && desktopworks) { fid = popen("xdg-user-dir DESKTOP","r"); // get desktop for user locale if (! fid) { zmessLogACK(Mwin,mess3,strerror(errno)); desktopworks = 0; } else { nn = fscanf(fid,"%s",dtdirk); pclose(fid); if (nn != 1) { zmessLogACK(Mwin,mess3,strerror(errno)); desktopworks = 0; } } if (desktopworks) { snprintf(trashdir,200,"%s/fotoxx-trash",dtdirk); // check if trash directory exists err = stat(trashdir,&statb); if (! S_ISDIR(statb.st_mode)) { err = mkdir(trashdir,0750); // create if not already if (err) { zmessLogACK(Mwin,mess3,strerror(errno)); desktopworks = 0; } } } if (desktopworks) { err = shell_ack("cp \"%s\" \"%s\" ",trash_file,trashdir); // copy image file to desktop trash if (err) desktopworks = 0; else { err = remove(trash_file); // remove original file if (err) { zmessLogACK(Mwin,ZTX("error: %s"),wstrerror(errno)); // cannot delete file goto KILL; } } } } if (! trashworks && ! desktopworks) { yn = zmessageYN(Mwin,mess2); // nothing works, ask to delete if (! yn) goto NEXT; // keep file err = remove(trash_file); // delete file if (err) { zmessLogACK(Mwin,ZTX("delete failed: \n %s"),wstrerror(errno)); goto KILL; } } delete_image_index(trash_file); // delete file in image index Nth = gallery_position(trash_file,0); // find in gallery list if (Nth >= 0) gallery(0,"delete",Nth); // delete from gallery list if (curr_file && strmatch(trash_file,curr_file)) // current file was trashed free_resources(); if (FGW == 'F') { // F window file = gallery(0,"find",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); } zd->zstat = 0; gtk_window_present(MWIN); // keep focus on main window return 1; } NEXT: if (FGW != 'F') goto KILL; // G gallery window m_next(0,0); // open next image gtk_window_present(MWIN); // keep focus on main window zd->zstat = 0; // keep dialog active return 1; KILL: zdialog_free(zd); // kill dialog zdtrash = 0; if (trash_file) zfree(trash_file); // free memory trash_file = 0; return 1; } /**************************************************************************/ // delete an image file, cannot be reversed char *delete_file = 0; void m_delete(GtkWidget *, cchar *) { int delete_dialog_event(zdialog *zd, cchar *event); cchar *title = ZTX("Delete Image File - CANNOT BE REVERSED"); cchar *autostep = ZTX("(automatic step to next image)"); F1_help_topic = "delete_image"; if (checkpend("all")) return; if (delete_file) zfree(delete_file); delete_file = 0; if (clicked_file) { // use clicked file if present delete_file = clicked_file; clicked_file = 0; } else if (curr_file) // else use current file delete_file = zstrdup(curr_file); else return; /*** ______________________________________ | Delete Image File | | CANNOT BE REVERSED | | | | Image File: [______________________] | | | | (automatic step to next image) | | | | [delete] [keep] [cancel] | |______________________________________| ***/ if (! zddelete) { zddelete = zdialog_new(title,Mwin,Bdelete,Bkeep,Bcancel,null); zdialog *zd = zddelete; 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"); if (FGW == 'F') zdialog_add_widget(zd,"label","labnext","dialog",autostep); zdialog_resize(zd,400,0); zdialog_run(zd,delete_dialog_event); // run dialog } char *pfile = strrchr(delete_file,'/'); if (pfile) pfile++; else pfile = delete_file; zdialog_stuff(zddelete,"file",pfile); gtk_window_present(MWIN); // keep focus on main window 15.03 return; } // dialog event and completion callback function int delete_dialog_event(zdialog *zd, cchar *event) { int err, Nth, yn; char *file; STATB statb; if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 if (zd->zstat == 0) return 1; // wait for dialog finished if (zd->zstat == 2) goto NEXT; // [keep] button if (zd->zstat != 1) goto KILL; // [cancel] or [x] if (! delete_file) goto KILL; // [delete] button err = stat(delete_file,&statb); // file exists? if (err) goto KILL; if (! S_ISREG(statb.st_mode)) goto KILL; if (! (statb.st_mode & S_IWUSR)) { // check permission yn = zmessageYN(Mwin,ZTX("Delete read-only file?")); if (! yn) goto NEXT; // keep file statb.st_mode |= S_IWUSR; // make writable if needed chmod(delete_file,statb.st_mode); } if (checkpend("all")) goto KILL; err = remove(delete_file); // delete file if (err) { zmessageACK(Mwin,ZTX("delete failed: \n %s"),wstrerror(err)); goto KILL; } delete_image_index(delete_file); // delete old file in image index Nth = gallery_position(delete_file,0); // delete from gallery list if (Nth >= 0) gallery(0,"delete",Nth); if (curr_file && strmatch(delete_file,curr_file)) { // current file was deleted last_file_posn = curr_file_posn; // remember for prev/next use 15.05 free_resources(); } if (FGW == 'F') { // F window file = gallery(0,"find",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); } zd->zstat = 0; gtk_window_present(MWIN); // keep focus on main window return 1; } NEXT: if (FGW != 'F') goto KILL; m_next(0,0); // open next image gtk_window_present(MWIN); // keep focus on main window zd->zstat = 0; // keep dialog active return 1; KILL: zdialog_free(zd); // kill dialog zddelete = 0; if (delete_file) zfree(delete_file); // free memory delete_file = 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 *) { F1_help_topic = "print_image"; print_calibrated(); 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 *) { if (clicked_file) { file_copytoclipboard(clicked_file); zfree(clicked_file); clicked_file = 0; } else if (curr_file) file_copytoclipboard(curr_file); 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 save_params(); // save state for next session zdialog_inputs("save"); // save dialog inputs zdialog_positions("save"); // save dialog positions free_resources(); // delete temp files fflush(null); // flush stdout, stderr exif_server(0,0,0); // kill exif_server process shell_quiet("rm -R -f %s",tempdir); // delete temp files gtk_main_quit(); // gone forever exit(1); // just in case } /**************************************************************************/ // 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("Edit Functions Summary"))) showz_textfile("data","edit-menus"); 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"))) zmessageACK(Mwin,"%s\n %s\n %s\n %s\n %s\n %s\n", Frelease,Flicense,Fhomepage,Fsoftware,Fcontact,Ftranslators); if (strmatch(menu,ZTX("Help"))) // menu button showz_userguide(F1_help_topic); // show topic if there, or page 1 return; } /**************************************************************************/ // 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. int preload_lock = 0; void f_preload(int next) // revised 15.01 { void * preload_thread(void *); char *file = 0; if (! curr_file) return; file = gallery_getnext(next,Flastversion); if (! file) return; if (strmatch(file,curr_file)) { zfree(file); return; } while (preload_lock) zsleep(0.01); // wait if preload thread busy 15.02 preload_lock = 1; start_detached_thread(preload_thread,file); return; } void * preload_thread(void *arg) { int fd; char *file = (char *) arg; 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); preload_lock = 0; // 15.02 pthread_exit(0); } /**************************************************************************/ // 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 func busy or edit func not restartable // 2 unsaved edits // 3 file not found or user cancel // 4 unsupported file type or open failure int f_open(cchar *filespec, int Nth, int Fkeep, int Fack, int zoom) { PXB *tempxb = 0; int err, fposn, ftyp, retcode = 0; static int Freent = 0; char *pp, *file; void (*menufunc)(GtkWidget *, cchar *); STATB statb; if (Freent++) { // stop re-entry printz("f_open() re-entry \n"); goto ret1; } if (clicked_file) zfree(clicked_file); clicked_file = 0; if (CEF && ! CEF->Frestart) goto ret1; // edit function not restartable if (Fthreadbusy) goto ret1; // do not interrupt thread if (checkpend("mods")) goto ret2; // keep unsaved changes menufunc = 0; if (CEF) menufunc = CEF->menufunc; // active edit, save menu function if (CEF && CEF->zd) zdialog_send_event(CEF->zd,"cancel"); // cancel if possible if (CEF) goto ret1; // cannot sa_unselect(); // unselect area if any 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; } ftyp = image_file_type(file); // must be image or RAW file type if (ftyp == 4) { if (Fack) zmessageACK(Mwin,"thumbnail file"); goto ret4; } if (ftyp != 2 && ftyp != 3) { // must be supported image file type if (Fack) zmessageACK(Mwin,ZTX("unknown file type")); // or RAW 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 15.03 fposn = gallery_position(file,Nth); // file position in gallery list if (fposn < 0) { // not there gallery(curr_file,"init"); // generate new gallery list fposn = gallery_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) copy_move(0); // " copy/move dialog if (zddelete) m_delete(0,0); // " delete dialog if (zdtrash) m_trash(0,0); // " trash dialog if (zdexifview) meta_view(0); // " EXIF/IPTC data view window if (zdeditmeta) m_edit_metadata(0,0); // " edit metadata dialog if (zdexifedit) m_meta_edit_any(0,0); // " edit any metadata dialog if (zdeditgeotags) m_edit_geotags(0,0); // " edit geotags dialog if (Fcaptions) m_captions(0,0); // show caption/comments at top m_viewmode(0,"F"); if (menufunc) menufunc(0,0); // restart edit function 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 (E0pxm) { // edits were made PXB_free(Fpxb); // new window image Fpxb = PXM_PXB_copy(E0pxm); } if (! f_save_file) return 1; // bugfix v.15.10 if (curr_file) zfree(curr_file); curr_file = zstrdup(f_save_file); // new current file fposn = gallery_position(curr_file,Nth); // file position in gallery list if (fposn < 0) { // not there gallery(curr_file,"init"); // generate new gallery list fposn = gallery_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 gtk_window_present(MWIN); if (zdexifview) meta_view(0); // update EXIF/IPTC view window 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[2] = { exif_orientation_key, exif_editlog_key }; cchar *exifdata[2]; char *ppv[1], *tempfile, *pext, *thumbfile; char edithist[exif_maxcc]; // edit history trail int nkeys, err, cc1, cc2, ii, ii0; int fmodified, fcopy; void (*menufunc)(GtkWidget *, cchar *); STATB fstat; cchar *warnalphamess = ZTX("Transparency map will be lost.\n" "save to PNG file to retain."); if (! curr_file) return 1; if (Fthreadbusy) return 1; // poss. edit thread running if (strmatch(outype,"RAW")) { // disallow saving as RAW type zmessageACK(Mwin,ZTX("cannot save as RAW type")); return 1; } fmodified = 0; menufunc = 0; if (CEF) { // edit function active if (CEF->Fmods) fmodified = 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) fmodified = 1; // completed edits not saved outfile = zstrdup(outfile,6); // output file name pext = strrchr(outfile,'/'); // force compatible file extension if (pext) pext = strrchr(pext,'.'); // if not already if (! pext) pext = outfile + strlen(outfile); if (strmatch(outype,"jpg") && ! strcasestr(".jpg .jpeg",pext)) // standardize output file .ext strcpy(pext,".jpg"); // .jpg .tif .png .bmp if (strmatch(outype,"tif") && ! strcasestr(".tif .tiff",pext)) strcpy(pext,".tif"); if (strmatch(outype,"png") && ! strcasestr(".png",pext)) strcpy(pext,".png"); if (strmatch(outype,"bmp") && ! strcasestr(".bmp",pext)) strcpy(pext,".bmp"); if (! fmodified) { // no unsaved edits fcopy = 1; // direct copy intput to output ? if (E0pxm) fcopy = 0; // non-edit change (upright) 15.03.1 if (! strmatch(curr_file_type,outype)) fcopy = 0; // no, file type change if (curr_file_bpc != outbpc) fcopy = 0; // no, BPC change if (jpeg_1x_quality < jpeg_def_quality) fcopy = 0; // no, higher compression wanted 15.03 if (fcopy) { if (strmatch(curr_file,outfile)) goto updateindex; // save to same file, skip copy err = shell_quiet("cp \"%s\" \"%s\"",curr_file,outfile); // copy unchanged file to output if (err) return 1; goto updateindex; } } Ffuncbusy = 1; // may be large file, slow CPU if (! E0pxm) { if (! resource_lock(Fpaintlock)) return 1; // 15.02 E0pxm = PXM_load(curr_file,1); // no prior edits, load image file resource_unlock(Fpaintlock); if (! E0pxm) { zfree(outfile); Ffuncbusy = 0; // PXM_load() diagnoses error return 1; } } tempfile = zstrdup(outfile,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 .bmp .jpg (no alpha channel) if (Fwarnalpha && E0pxm->nc > 3) { ii = zdialog_choose(Mwin,warnalphamess,Bsave,Bcancel,null); // 15.09 if (ii != 1) { remove(tempfile); // NO zfree(tempfile); zfree(outfile); Ffuncbusy = 0; return 1; } } err = PXM_ANY_save(E0pxm,tempfile); // (bpc = 8 always) outbpc = 8; } if (err) { if (*file_errmess) // pass error to user if (Fack) zmessageACK(Mwin,file_errmess); remove(tempfile); // failure, clean up zfree(tempfile); zfree(outfile); Ffuncbusy = 0; return 1; } exif_get(curr_file,&exifkey[1],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 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; } } exifdata[0] = ""; // remove EXIF orientation if present nkeys = 1; // (assume saved file is upright) if (cc1) { // prior and/or curr. edit hist 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,outfile); // rename temp file to output file if (err) { if (Fack) zmessageACK(Mwin,wstrerror(err)); remove(tempfile); // delete temp file zfree(tempfile); zfree(outfile); 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; thumbfile = image_thumbfile(outfile); // update thumbnail file if (thumbfile) zfree(thumbfile); updateindex: load_filemeta(outfile); // load output file metadata 15.02 update_image_index(outfile); // add output file to image index 15.08 if (navi::gallerytype == 1 && samedirk(outfile,curr_file)) { // output to curr. directory gallery(curr_file,"init"); // update curr. gallery list set_mwin_title(); // update posn, count in title } add_recent_file(outfile); // first in recent files list if (f_save_file) zfree(f_save_file); // actual file saved (.ext may change) f_save_file = outfile; strcpy(f_save_type,outype); // update f_save_xxx data f_save_bpc = outbpc; err = stat(outfile,&fstat); f_save_size = fstat.st_size; if (! strmatch(curr_file,outfile)) // restore metadata for curr. file 15.03 load_filemeta(curr_file); 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 *outfile = 0, *outfile2 = 0, *pp, *pext; int ii, zstat, err, yn; int bpc, mkcurr = 0; STATB fstat; zthreadcrash(); /*** ____________________________________________________________________ | | | Save as New File Name or Type | | | | [ file chooser dialog goes here ] | | | | ꖴ tif-8 ꖴ tif-16 ꖴ png-8 ꖴ png-16 ꖴ bmp ꖴ jpg quality [90] | | [x] make current | | | | [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"); zdialog_add_widget(zd,"vbox","vbfc","hbfc",0,"space=3"); 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,"hbox","hbft","dialog",0,"space=3"); zdialog_add_widget(zd,"radio","tif-8","hbft","tif-8","space=3"); zdialog_add_widget(zd,"radio","tif-16","hbft","tif-16","space=3"); zdialog_add_widget(zd,"radio","png-8","hbft","png-8","space=3"); zdialog_add_widget(zd,"radio","png-16","hbft","png-16","space=3"); zdialog_add_widget(zd,"radio","bmp","hbft","bmp","space=3"); zdialog_add_widget(zd,"radio","jpg","hbft","jpg","space=3"); zdialog_add_widget(zd,"label","labqual","hbft","quality"); zdialog_add_widget(zd,"entry","jpgqual","hbft","90","space=2|size=3"); zdialog_add_widget(zd,"check","mkcurr","hbft",ZTX("make current"),"space=3"); if (! save_dirk) { // default from curr. file save_dirk = zstrdup(curr_file); pp = strrchr(save_dirk,'/'); *pp = 0; } gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(saveas_fchooser),save_dirk); char *fname = strrchr(curr_file,'/') + 1; gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(saveas_fchooser),fname); 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,"bmp",0); 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 if (strmatch(curr_file_type,"bmp")) // and bmp zdialog_stuff(zd,"bmp",1); else zdialog_stuff(zd,"jpg",1); // else default jpg zdialog_stuff(zd,"mkcurr",0); // deselect "make current" zdialog_resize(zd,500,500); zdialog_run(zd,f_save_as_dialog_event); 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; } zdialog_fetch(zd,"bmp",ii); if (ii) type = "bmp"; if (strmatch(type,"jpg")) { // set non-default jpeg save quality zdialog_fetch(zd,"jpgqual",ii); // will be used only one time if (ii < 1 || ii > 100) { zmessageACK(Mwin,ZTX("jpeg quality must be 1-100")); zd->zstat = 0; goto zdialog_wait; } 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 (strmatch(type,"bmp") && ! strcasestr(".bmp",pext)) *pext = 0; } if (! pext || ! *pext) { pext = outfile + strlen(outfile); // wrong or missing, add new .ext *pext = '.'; // no replace .JPG with .jpg etc. strcpy(pext+1,type); } zdialog_fetch(zd,"mkcurr",mkcurr); // get make current option err = stat(outfile,&fstat); // 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"); // refresh gallery list curr_file_posn = gallery_position(curr_file,curr_file_posn); // update curr. file position set_mwin_title(); // update window title } 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[6] = { "tif-8", "tif-16", "png-8", "png-16", "bmp", "jpg" }; char ext[4]; char *filespec; char *filename, *pp; if (strmatch(event,"enter")) zd->zstat = 1; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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 < 6; ii++) // detect file type selection if (strmatch(event,filetypes[ii])) break; if (ii == 6) 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,"bmp",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; } /************************************************************************** Find all image files within given path. dirk directory path to search NF count of filespecs returned flist list of filespecs returned Fthumbs include thumbnail files if Fthumbs = 1 Finit initialize flag (omit, default = 1) (0 is used for internal recursive calls) Hidden directories and files (dotfiles) are suppressed. 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_count1; // filelist slots allocated int fif_count2; // filelist slots filled int find_imagefiles(cchar *dirk, char **&flist, int &NF, int Fthumbs, int Finit) { int flags = GLOB_NOSORT; int err, err2, cc, ftype; char *file, *mdirk, **templist; glob_t globdata; STATB statdat; if (Finit) fif_count1 = fif_count2 = 0; globdata.gl_pathc = 0; // glob() setup globdata.gl_offs = 0; globdata.gl_pathc = 0; NF = 0; flist = 0; mdirk = zstrdup(dirk,4); // append /* to input directory strcat(mdirk,"/*"); err = glob(mdirk,flags,null,&globdata); // find all files in directory if (err) { if (err == GLOB_NOMATCH) err = 0; else if (err == GLOB_ABORTED) err = 1; else if (err == GLOB_NOSPACE) err = 2; else err = 3; goto fif_return; } for (uint ii = 0; ii < globdata.gl_pathc; ii++) // loop found files { file = globdata.gl_pathv[ii]; err = stat(file,&statdat); if (err) continue; if (S_ISDIR(statdat.st_mode)) { // directory, call recursively err = find_imagefiles(file,flist,NF,Fthumbs,0); if (err) goto fif_return; continue; } ftype = image_file_type(file); // check file type is image, if (ftype < 2 || ftype > 4) continue; // RAW, or thumbnail file if (! Fthumbs && ftype == 4) continue; // exclude thumbnail files if (fif_count2 == fif_count1) { // output list is full if (fif_count1 == 0) { fif_count1 = 1000; // initial space, 1000 files cc = fif_count1 * sizeof(char *); fif_filelist = (char **) zmalloc(cc); } else { templist = fif_filelist; // expand by 2x each time needed cc = fif_count1 * sizeof(char *); fif_filelist = (char **) zmalloc(cc+cc); memcpy(fif_filelist,templist,cc); memset(fif_filelist+fif_count1,0,cc); zfree(templist); fif_count1 *= 2; } } fif_filelist[fif_count2] = zstrdup(file); // add file to output list fif_count2 += 1; } err = 0; fif_return: err2 = errno; // preserve Linux errno globfree(&globdata); // free memory zfree(mdirk); if (Finit) { NF = fif_count2; if (NF) flist = fif_filelist; } errno = err2; return err; } /**************************************************************************/ // 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; } /************************************************************************** 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, ftyp; cchar *pext; PXB *pxb; STATB fstat; err = stat(file,&fstat); if (err) { if (Fack) zmessageACK(Mwin,ZTX("file not found: %s"),file); goto f_load_fail; } ftyp = image_file_type(file); if (ftyp != 2 && ftyp != 3) { if (Fack) zmessageACK(Mwin,ZTX("file type not supported: %s"),file); goto f_load_fail; } f_load_size = fstat.st_size; // set f_load_size pext = strrchr(file,'/'); if (! pext) pext = file; pext = strrchr(pext,'.'); if (! pext) pext = ""; if (ftyp == 2) { 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 (ftyp == 3) strcpy(f_load_type,"RAW"); // f_load_type = RAW 15.08 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, ftyp; cchar *pext; PXM *pxm; STATB fstat; err = stat(file,&fstat); if (err) { if (Fack) zmessageACK(Mwin,ZTX("file not found: %s"),file); goto f_load_fail; } ftyp = image_file_type(file); if (ftyp != 2 && ftyp != 3) { if (Fack) zmessageACK(Mwin,ZTX("file type not supported: %s"),file); goto f_load_fail; } f_load_size = fstat.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 (ftyp == 3) // 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_limit || hh > wwhh_limit) { snprintf(file_errmess,999,"image too big, %dx%d: %s",ww,hh,file); printz("%s\n",file_errmess); 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; // 15.08 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; // bugfix 15.09.2 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 (ww > wwhh_limit || hh > wwhh_limit) { snprintf(file_errmess,999,"image too big, %dx%d: %s",ww,hh,file); printz("%s\n",file_errmess); 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_PXM_load(file); // fallback to pixbuf loader } if (nc == 2 || nc == 4) ac = 1; // alpha channel 15.08 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); // 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; } pxm = PXM_make(ww,hh,3+ac); // create PXM 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]; pix += 3+ac; tiff8 += 4; } } zfree(tiffbuff); return pxm; } pxm = PXM_make(ww,hh,3+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); 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; int tiffstat = 0; int ww, hh, row, col, rowcc; // int not uint int nc, ac, pm = 2, pc = 1, comp = 5; char *tiffbuff; 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; // 15.08 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); // LZW 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_limit || hh > wwhh_limit) { png_destroy_read_struct(&pngstruct,&pnginfo,0); snprintf(file_errmess,999,"image too big, %dx%d: %s",ww,hh,file); printz("%s\n",file_errmess); 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 15.08 } 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 15.08 } 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 15.08 } 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 (ww > wwhh_limit || hh > wwhh_limit) { png_destroy_read_struct(&pngstruct,&pnginfo,0); snprintf(file_errmess,999,"image too big, %dx%d: %s",ww,hh,file); printz("%s\n",file_errmess); return 0; } 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 (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) { // 15.09 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; // 15.08 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); // 15.09 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; zthreadcrash(); *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); // 15.08 ac = gdk_pixbuf_get_has_alpha(pixbuf); rs = gdk_pixbuf_get_rowstride(pixbuf); pixels1 = gdk_pixbuf_get_pixels(pixbuf); if (ww > wwhh_limit || hh > wwhh_limit) { snprintf(file_errmess,999,"image too big, %dx%d: %s",ww,hh,file); printz("%s\n",file_errmess); 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]; zthreadcrash(); 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); // 15.08 ac = gdk_pixbuf_get_has_alpha(pixbuf); rowst = gdk_pixbuf_get_rowstride(pixbuf); pixels1 = gdk_pixbuf_get_pixels(pixbuf); if (ww > wwhh_limit || hh > wwhh_limit) { snprintf(file_errmess,999,"image too big, %dx%d: %s",ww,hh,file); printz("%s\n",file_errmess); g_object_unref(pixbuf); return 0; } pxm = PXM_make(ww,hh,3+ac); 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; zthreadcrash(); *file_errmess = 0; ww = pxm->ww; hh = pxm->hh; nc = pxm->nc; // 15.08 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 15.09 pxbstat = gdk_pixbuf_save(pixbuf,file,"png",&gerror,"compression","1",null); else if (strstr(".bmp .BMP",pext)) pxbstat = gdk_pixbuf_save(pixbuf,file,"bmp",&gerror,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) ***************************************************************************/ // Load PXB (pixbuf, 8 bit color) from RAW file using DCraw program // f_load_bpc is set to 16 PXB * RAW_PXB_load(cchar *rawfile) { PXB *pxb; int handle, err; char *pp, rawcommand[200]; strcpy(rawcommand,DCrawcommand); // change default DCraw command to pp = strstr(rawcommand,"-6 "); // output tif-8 instead of tif-16 if (pp) strcpy(pp, DCrawcommand + (pp+3-rawcommand)); handle = shell_asynch("%s -c \"%s\" >temp.tif",rawcommand,rawfile); // convert RAW to tif-8 file while (true) { err = shell_asynch_status(handle); if (err != -1) break; zsleep(0.01); zmainloop(); } pxb = TIFF_PXB_load("temp.tif"); // load the tiff file f_load_bpc = 16; // RAW file bpc remove("temp.tif"); // delete the tiff file return pxb; } // Load PXM (float color) from RAW file using DCraw program // f_load_bpc is set to 16 PXM * RAW_PXM_load(cchar *rawfile) { PXM *pxm; int handle, err; handle = shell_asynch("%s -c \"%s\" >temp.tif",DCrawcommand,rawfile); // convert RAW to tiff-16 file while (true) { err = shell_asynch_status(handle); if (err != -1) break; zsleep(0.1); zmainloop(); } pxm = TIFF_PXM_load("temp.tif"); // load the tiff file f_load_bpc = 16; remove("temp.tif"); // delete the tiff file return pxm; } fotoxx-15.11.1/appdata/0000755000175000017500000000000012616075370013270 5ustar micomicofotoxx-15.11.1/appdata/fotoxx.appdata.xml0000644000175000017500000000216212616075370016753 0ustar micomico fotoxx.desktop CC0-1.0 GPL-3.0+ Fotoxx Photo Editor and Colletion Manager

Edit photos and manage a large image collection. Includes a thumbnail browser/navigator, RAW file import, a comprehensive set of edit functions working in deep color, rapid response, file versioning, edit/copy/paste selected objects or areas, batch operations, albums (alternate views), metadata edit and report, composite images (HDR, stack, panorama, mashup), special effects and arty transformations, fast image searching using any metadata and/or directory/file names.

http://www.weebly.com/uploads/1/3/0/3/13035936/fotoxx-appdata-screenshot.jpg http://www.kornelix.com/fotoxx
fotoxx-15.11.1/f.area.cc0000664000175000017500000034417712616075370013343 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** 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_unfinish unfinish selected areas 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 m_select_open open PNG file and make a select area m_select_save save area to PNG file with alpha channel 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 | | [x] select matching color within mouse: [####] | | [x] select all matching colors within mouse: | | mouse radius [___+-] match level [___+-] % | | mouse search range [___+|-] [x] firewall | | 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"),"space=5"); zdialog_add_widget(zd,"hbox","hbm2","dialog"); zdialog_add_widget(zd,"check","ckonecolor","hbm2",ZTX("select one matching color within mouse"),"space=5"); zdialog_add_widget(zd,"colorbutt","onecolor","hbm2","0|0|255","space=5"); zdialog_add_widget(zd,"hbox","hbm3","dialog"); zdialog_add_widget(zd,"check","ckallcolors","hbm3",ZTX("select all matching colors within mouse"),"space=5"); zdialog_add_widget(zd,"hbox","hbmm","dialog"); zdialog_add_widget(zd,"label","space","hbmm",0,"space=15"); zdialog_add_widget(zd,"label","labmr","hbmm",ZTX("mouse radius")); zdialog_add_widget(zd,"spin","mouserad","hbmm","1|300|1|20","space=5|size=2"); zdialog_add_widget(zd,"label","space","hbmm",0,"space=10"); zdialog_add_widget(zd,"label","labmatch","hbmm",ZTX("match level %")); zdialog_add_widget(zd,"spin","colormatch","hbmm","0|100|1|90","space=5|size=2"); zdialog_add_widget(zd,"hbox","hbm4","dialog"); zdialog_add_widget(zd,"label","space","hbm4",0,"space=15"); zdialog_add_widget(zd,"label","labrange","hbm4",ZTX("search range")); zdialog_add_widget(zd,"spin","searchrange","hbm4","1|20|1|3","space=5|size=1"); zdialog_add_widget(zd,"check","firewall","hbm4",ZTX("firewall"),"space=20"); zdialog_add_widget(zd,"hbox","hbm5","dialog"); zdialog_add_widget(zd,"label","labblend","hbm5",ZTX("Area Edge Blend Width"),"space=5"); zdialog_add_widget(zd,"spin","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"); // 15.09 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_firewall = 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); // show existing area, if any 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 (FGW != 'F') event = "done"; if (strmatch(event,"escape")) event = "done"; // 15.07 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 15.11 for (ii = 0; ii < cc; ii++) if (sa_pixmap[ii]) break; if (ii == cc) { sa_stat = 0; // none, set no area active zfree(sa_pixmap); } } 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 15.08 sa_stat = 1; // active edit status sa_Npixel = sa_blend = sa_calced = 0; // make area unfinished zdialog_stuff(zd,"blendwidth",0); sa_show(1); } 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,"firewall")) // mouse color match firewall on/off zdialog_fetch(zdsela,"firewall",sa_firewall); if (strmatch(event,"show")) sa_show(1); // show area if (strmatch(event,"hide")) sa_show(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; // 15.07 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, crflag = 0; if (sa_stat != 1) return; // area gone? if (sa_currseq > sa_maxseq-2) { zmessageACK(Mwin,ZTX("exceed %d edits"),sa_maxseq); // cannot continue return; } if (RMclick) // right mouse click { RMclick = 0; sa_unselect_pixels(); // remove latest selection drag = 0; Fpaint2(); return; } if (! Mxdrag && ! Mydrag) return; // no drag underway 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; return; } mx2 = Mxdrag; // drag continues, 2nd corner my2 = Mydrag; Mxdrag = Mydrag = 0; if (drag) sa_unselect_pixels(); // remove prior drag result sa_nextseq(); // next sequence number drag = 1; if (sa_mode == mode_rect) // draw rectangle { if (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already cairo_set_line_width(mwcr,1); crflag = 1; } sa_draw_line(mx1,my1,mx2,my1); // draw 4 lines sa_draw_line(mx2,my1,mx2,my2); // (makes own cairo_t) sa_draw_line(mx2,my2,mx1,my2); sa_draw_line(mx1,my2,mx1,my1); if (crflag) { cairo_destroy(mwcr); mwcr = 0; } } if (sa_mode == mode_ellipse) // draw ellipse { if (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already cairo_set_line_width(mwcr,1); crflag = 1; } 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); // draw 2 points on ellipse px = cx + x + 0.5; sa_draw1pix(px,py); } 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); // draw 2 points on ellipse py = cy + y + 0.5; sa_draw1pix(px,py); } if (crflag) { cairo_destroy(mwcr); mwcr = 0; } } 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); void sa_redraw(int mx1, int my1, int mx2, int my2); int mx1, my1, mx2, my2; int ii, npdist, npx, npy; int click, newseq, thresh; int crflag = 0; static int drag = 0, openend = 0; static int mdx0, mdy0, mdx1, mdy1; if (sa_stat != 1) return; // area gone? sa_thresh = 4.0 / Mscale + 1; // mouse pixel distance threshold if (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already crflag = 1; } 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); } 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); // 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); // 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); // draw line from end pixel to mouse if (sa_mode == mode_follow) sa_follow_edge(mx1,my1,mx2,my2); // follow edge from end pixel to mouse if (sa_mode == mode_replace) sa_redraw(mx1,my1,mx2,my2); // 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(); // remove latest selection draw_exit: LMclick = RMclick = 0; // stop further mouse action Mxdrag = Mydrag = 0; if (crflag) { cairo_destroy(mwcr); mwcr = 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) { int pxm, pym, crflag = 0; float slope; if (sa_stat != 1) return; // area gone? if (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already cairo_set_line_width(mwcr,1); crflag = 1; } if (px1 == px2 && py1 == py2) { // only one pixel sa_draw1pix(px1,py1); if (crflag) { cairo_destroy(mwcr); mwcr = 0; } 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); } } else { for (pym = py1; pym >= py2; pym--) { pxm = round(px1 + slope * (pym - py1)); sa_draw1pix(pxm,pym); } } } 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); } } else { for (pxm = px1; pxm >= px2; pxm--) { pym = round(py1 + slope * (pxm - px1)); sa_draw1pix(pxm,pym); } } } if (crflag) { cairo_destroy(mwcr); mwcr = 0; } 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) { 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); // 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) { float sa_get_contrast(int px, int py); float px3, py3, px4, py4, px5, py5, px6, py6; float dx, dy, dist, contrast, maxcontrast; if (sa_stat != 1) return; // area gone? 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); // draw p3 to p6 px3 = px6; // next p3 py3 = py6; } px2 = px3; // return lagging end point py2 = py3; return; } // freehand draw while erasing nearby pixels, effectively replacing them void sa_redraw(int mx1, int my1, int mx2, int my2) { int ii, npx, npy; int thresh, npdist, d1, d2; 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); } } sa_draw_line(mx1,my1,mx2,my2); // 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) return; d1 = (npx-mx1)*(npx-mx1) + (npy-my1)*(npy-my1); d2 = (npx-mx2)*(npx-mx2) + (npy-my2)*(npy-my2); if (d2 >= d1) return; ii = npy * Fpxb->ww + npx; // motion toward pixel sa_pixmap[ii] = 0; // unmap pixel erase_pixel(npx,npy); // erase pixel } 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, crflag = 0; 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 (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already cairo_set_line_width(mwcr,1); crflag = 1; } 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 within mouse + matching colors beyond } if (RMclick) { // right mouse click RMclick = 0; sa_unselect_pixels(); // remove latest selection sa_show(1); } 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 within 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(); // re-establish area edge } } draw_mousecircle(Mxposn,Myposn,sa_mouseradius,0); // move mouse circle with mouse if (crflag) { cairo_destroy(mwcr); mwcr = 0; } 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 within 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(); // re-establish edge Fpaint4(px-mrad,py-mrad,2*mrad,2*mrad); // 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 15.08 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 by descending count of matching pixels thresh = 0.03 * Ntab; // exclude minority pixels 15.08 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; if (select && sa_firewall && sa_pixmap[ii] && rad2 > mrad2) // aleady selected, firewall on, continue; // and outside mouse radius 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); // 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() { int px, py, xlo, xhi, ylo, yhi; xlo = Fpxb->ww; xhi = 0; ylo = Fpxb->hh; yhi = 0; if (sa_stat != 1) return; // area gone? if (! sa_currseq) return; // no pixels mapped 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); // 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 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 *safinzd = 0; void sa_finish() // overhauled { void sa_finish_mousefunc(); int sa_finish_dialog_event(zdialog *, cchar *event); 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 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); cc = (sa_maxx-sa_minx) * (sa_maxy-sa_miny); // allocate stack memory if (sa_stackdirec) zfree(sa_stackdirec); sa_stackdirec = (char *) zmalloc(cc); if (sa_stackii) zfree(sa_stackii); sa_stackii = (int *) zmalloc(cc * 4); sa_maxstack = cc; sa_fincancel = sa_finOK = sa_finhole = 0; // count areas OK and with holes safinzd = zdialog_new(ZTX("finish area"),pwin,Bdone,Bcancel,null); // dialog for user to click inside zdialog_add_widget(safinzd,"hbox","hbmess","dialog",0,"space=3"); // each enclosed area zdialog_add_widget(safinzd,"label","fmess","hbmess",fmess,"space=5"); zdialog_add_widget(safinzd,"hbox","hbcor","dialog"); zdialog_add_widget(safinzd,"vbox","vbcor1","hbcor",0,"space=5"); zdialog_add_widget(safinzd,"vbox","vbcor2","hbcor",0,"space=5"); zdialog_add_widget(safinzd,"vbox","vbcor3","hbcor",0,"space=5"); zdialog_add_widget(safinzd,"label","labcor","vbcor1",ZTX("extend to corner:"),"space=10"); zdialog_add_widget(safinzd,"check","NWcorner","vbcor2"); zdialog_add_widget(safinzd,"check","SWcorner","vbcor2"); zdialog_add_widget(safinzd,"check","NEcorner","vbcor3"); zdialog_add_widget(safinzd,"check","SEcorner","vbcor3"); zdialog_add_widget(safinzd,"hbox","hbstat","dialog"); zdialog_add_widget(safinzd,"label","labstat","hbstat","status:","space=5"); zdialog_add_widget(safinzd,"label","statmess","hbstat",0); takeMouse(sa_finish_mousefunc,dragcursor); // connect mouse function zdialog_run(safinzd,sa_finish_dialog_event,"parent"); // run dialog, parallel zdialog_wait(safinzd); zdialog_free(safinzd); // free dialog return; } // mouse function - get user clicks and perform pixel searches void sa_finish_mousefunc() // overhauled { void * sa_finish_thread(void *); int ii, px, py, crflag = 0; int npaint; if (! LMclick) return; LMclick = 0; if (! sa_stat) return; // area gone? 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 zdialog_stuff(safinzd,"statmess",ZTX("searching")); start_detached_thread(sa_finish_thread,0); // start pixel search thread while (sa_Nstack) zsleep(0.1); // wait for completion if (sa_fincancel) { // user cancel sa_finhole = sa_finOK = 0; return; } if (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already cairo_set_line_width(mwcr,1); crflag = 1; } 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); } if (crflag) { cairo_destroy(mwcr); mwcr = 0; } if (sa_finhole) { // this area has a hole zdialog_stuff(safinzd,"statmess",ZTX("outline has a gap")); sa_show(1); // improve visibility } else { zdialog_stuff(safinzd,"statmess",ZTX("success")); // all pixels found and mapped sa_finOK++; } return; } // thread function to map all pixels found within defined edge pixels. void * sa_finish_thread(void *) // thread function { int px, py, ii, jj, kk; int ppx, ppy, npx, npy; char direc; while (sa_Nstack) // find all pixels outside enclosed area(s) { if (sa_fincancel) break; // user cancel 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 (px < sa_minx || px >= sa_maxx || py < sa_miny || py >= sa_maxy) { sa_finhole++; // ran off the edge, seed pixel was break; // not inside area or area has a hole } 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 15.09 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 15.09 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 0; } // dialog event and completion callback function int sa_finish_dialog_event(zdialog *zd, cchar *event) { int ii, zstat; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"NWcorner")) // automatic extend to selected corners sa_pixmap[0] = 1; if (strmatch(event,"NEcorner")) { ii = Fpxb->ww - 1; sa_pixmap[ii] = 1; } if (strmatch(event,"SEcorner")) { ii = Fpxb->ww * (Fpxb->hh - 1); sa_pixmap[ii] = 1; } if (strmatch(event,"SWcorner")) { ii = Fpxb->ww * Fpxb->hh - 1; sa_pixmap[ii] = 1; } if (strstr(event,"corner")) sa_map_pixels(); // find edge pixels zstat = zd->zstat; // wait for completion if (! zstat) return 0; if (zstat != 1) sa_fincancel = 1; // kill thread function freeMouse(); // disconnect mouse if (! sa_stat) return 0; // area gone? if (sa_fincancel || sa_finOK == 0 || sa_finhole) { // user cancel or pixel search failure sa_unfinish(); // unmap interior pixels, set edit mode return 0; } sa_map_pixels(); // count pixels, map interior pixels if (sa_Npixel < 10) { zmessageACK(Mwin,ZTX("found %d pixels"),sa_Npixel); return 0; } 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 0; } // 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); 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 15.09 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; } // 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); return; } void m_select_hide(GtkWidget *, cchar *menu) { F1_help_topic = "area_show_hide"; sa_show(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) { 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 (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already 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 15.09 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); } if (crflag) { cairo_destroy(mwcr); mwcr = 0; } 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) { int px, py, px2, py2, ii, kk; 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; 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 15.09 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); } 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); 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); 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 15.09 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); 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_run(zd,edgecalc_dialog_event); 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 15.09 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 (strmatch(event,"escape")) zd->zstat = 1; // escape = cancel 15.07 if (! zd->zstat) return 0; Fkillfunc = 1; printz("*** edge calc killed \n"); 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) // process 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; float slope; mindist = 9999; mindist2 = mindist * mindist; epx = epy = 0; for (ii = 0; ii < sa_Nedge; ii++) // loop all edge pixels { 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 * Fpxb->ww + pxm; if (sa_edgedist[ii]) break; 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 * Fpxb->ww + pxm; if (sa_edgedist[ii]) break; 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 void sa_finish_auto(); // finish area again sa_show(1); sa_calced = 0; // invalidate prior edge calc. Fpaint2(); return; } /************************************************************************** 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_size; // size, 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; } // copy the current select area for later paste void m_select_copy(GtkWidget *, cchar *) // 15.09 { using namespace sa_diskfile; int ii, px1, py1, px2, py2, dist; int nc = 4, pcc = nc * sizeof(float); float *pix1, *pix2; char filename[100]; F1_help_topic = "area_copy_paste"; 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 if (! resource_lock(Fpaintlock)) return; // 15.02 E0pxm = PXM_load(curr_file,1); resource_unlock(Fpaintlock); if (! E0pxm) return; } PXM_free(sacp_pxm); // free prior if any PXM_free(sacpR_pxm); sacp_ww = sa_maxx - sa_minx + 1; // new area image PXM sacp_hh = sa_maxy - sa_miny + 1; sacp_pxm = PXM_make(sacp_ww,sacp_hh,nc); for (py2 = 0; py2 < sacp_hh; py2++) for (px2 = 0; px2 < sacp_ww; px2++) { px1 = px2 + sa_minx; py1 = py2 + sa_miny; pix1 = PXMpix(E0pxm,px1,py1); // copy area pixels into area PXM pix2 = PXMpix(sacp_pxm,px2,py2); memcpy(pix2,pix1,pcc); // copy RGB data to pix2[0] [1] [2] ii = py1 * Fpxb->ww + px1; dist = sa_pixmap[ii]; // 0/1/2+ = outside/edge/inside distance if (dist == 0) pix2[3] = 0; // outside pixel, transparent else if (dist == 1) pix2[3] = 128; // edge pixel, opacity = 50% else pix2[3] = 255; // inside pixel, opacity = 100% } snprintf(filename,100,"%s/copied_area.png",saved_areas_dirk); PXM_PNG_save(sacp_pxm,filename,16); return; } // paste the area last copied on to the current image void m_select_paste(GtkWidget *, cchar *) // 15.09 { 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; } sacp_ww = pxmtemp->ww; // image dimensiona sacp_hh = pxmtemp->hh; sacp_pxm = PXM_make(sacp_ww,sacp_hh,5); // make PXM with 2 extra channels nc = pxmtemp->nc; pcc = nc * sizeof(float); // 3 or 4 channels, RGB or RGBA 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 == 3) pix2[3] = 255; // if no alpha channel, all pixels opaque } PXM_free(pxmtemp); select_paste(0,0); // interactive move/resize area image return; } // Read a select area from a disk PNG file. void m_select_open(GtkWidget *, cchar *) // 15.08 { 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 = zgetfile(ZTX("load area from a file"),MWIN,"file",saved_areas_dirk); if (! file) return; pxmtemp = ANY_PXM_load(file); // load image file zfree(file); if (! pxmtemp) { if (*file_errmess) zmessageACK(Mwin,file_errmess); return; } sacp_ww = pxmtemp->ww; // image dimensiona sacp_hh = pxmtemp->hh; sacp_pxm = PXM_make(sacp_ww,sacp_hh,5); // make PXM with 2 extra channels nc = pxmtemp->nc; pcc = nc * sizeof(float); // 3 or 4 channels, RGB or RGBA 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 == 3) pix2[3] = 255; // if no alpha channel, all pixels opaque } PXM_free(pxmtemp); select_paste(0,0); // interactive move/resize area image return; } // Save a select area to a disk PNG file. void m_select_save(GtkWidget *, cchar *) // 15.08 { using namespace sa_diskfile; int ii, px1, py1, px2, py2, dist; int nc = 4, pcc = nc * sizeof(float); float *pix1, *pix2; char *pp, *file; F1_help_topic = "area_open_save"; 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 if (! resource_lock(Fpaintlock)) return; // 15.02 E0pxm = PXM_load(curr_file,1); resource_unlock(Fpaintlock); if (! E0pxm) return; } PXM_free(sacp_pxm); // free prior if any PXM_free(sacpR_pxm); sacp_ww = sa_maxx - sa_minx + 1; // new area image PXM sacp_hh = sa_maxy - sa_miny + 1; sacp_pxm = PXM_make(sacp_ww,sacp_hh,nc); for (py2 = 0; py2 < sacp_hh; py2++) for (px2 = 0; px2 < sacp_ww; px2++) { px1 = px2 + sa_minx; py1 = py2 + sa_miny; pix1 = PXMpix(E0pxm,px1,py1); // copy area pixels into area PXM pix2 = PXMpix(sacp_pxm,px2,py2); memcpy(pix2,pix1,pcc); // copy RGB data to pix2[0] [1] [2] ii = py1 * Fpxb->ww + px1; dist = sa_pixmap[ii]; // 0/1/2+ = outside/edge/inside distance if (dist == 0) pix2[3] = 0; // outside pixel, transparent else if (dist == 1) pix2[3] = 128; // edge pixel, opacity = 50% else pix2[3] = 255; // inside pixel, opacity = 100% } pp = zgetfile(ZTX("save area as a PNG file"),MWIN,"save",saved_areas_dirk); 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); zfree(file); 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(); void select_paste_makearea(); int select_paste_dialog_event(zdialog *, cchar *event); void select_paste_mousefunc(); int px1, py1, px2, py2, ii; float *pix2; 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_size = 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 PXM_free(sacpR_pxm); // free prior if any if (sacp_ww > E0pxm->ww) // if paste image > current image 15.10 sacp_size = 1.0 * E0pxm->ww / sacp_ww; // scale down to fit if (sacp_hh * sacp_size > E0pxm->hh) sacp_size = 1.0 * E0pxm->hh / sacp_hh; if (sacp_size < 1.0) { sacp_ww *= sacp_size; sacp_hh *= sacp_size; sacpR_pxm = PXM_rescale(sacp_pxm,sacp_ww,sacp_hh); PXM_free(sacp_pxm); sacp_pxm = sacpR_pxm; sacp_size = 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 (initially invisible) select_paste_makearea(); // make a regular select area sa_edgecalc(); // calculate pixel edge distances for (py2 = 0; py2 < sacp_hh; py2++) // save edge distance in 5th channel for (px2 = 0; px2 < sacp_ww; px2++) { px1 = px2 + sacp_orgx; py1 = py2 + sacp_orgy; ii = py1 * Fpxb->ww + px1; pix2 = PXMpix(sacp_pxm,px2,py2); pix2[4] = sa_pixmap[ii]; pix2 = PXMpix(sacpR_pxm,px2,py2); pix2[4] = sa_pixmap[ii]; } sa_unselect(); select_paste_image(); // make area image visible 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,"80/20"); // 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_makearea(); 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 (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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(); return 1; } edit_done(0); // commit the edit (pasted image) select_paste_makearea(); // make equivalent select area 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_size *= 1.001; if (strmatch(event,"+1%")) sacp_size *= 1.01; if (strmatch(event,"+10%")) sacp_size *= 1.10; if (strmatch(event,"-.1%")) sacp_size *= 0.999001; if (strmatch(event,"-1%")) sacp_size *= 0.990099; if (strmatch(event,"-10%")) sacp_size *= 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_size * sacp_ww; // new size hh = sacp_size * 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 } if (strmatch(event,"blend") && sacp_porg) { zdialog_fetch(zd,"blend",sacp_blend); // new edge blend distance select_paste_image(); // copy onto target image } if (strmatch(event,"brite") && sacp_porg) { zdialog_fetch(zd,"brite",sacp_brite); // new brightness adjustment select_paste_image(); // copy onto target image } 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; 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 Mxdrag = Mydrag = 0; return; } // copy select area into edit image, starting at sacp_orgx/y void select_paste_image() { using namespace sa_diskfile; int px1, py1, px2, py2, nc, pcc; float *pix1, *pix2, *pix3; float f1, f2, dist; float red, green, blue, maxrgb; 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); } } 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 image pixel red = pix2[0]; green = pix2[1]; blue = pix2[2]; if (sacp_brite != 1.0) { // adjust brightness of paste image red *= sacp_brite; // do before edge blend green *= sacp_brite; blue *= sacp_brite; maxrgb = red; if (green > maxrgb) maxrgb = green; if (blue > maxrgb) maxrgb = blue; if (maxrgb > 255.9) { f1 = 255.9 / maxrgb; red *= f1; green *= f1; blue *= f1; } } pix3 = PXMpix(E3pxm,px1,py1); // E3 image pixel f1 = pix2[3] / 255.0; // opacity dist = pix2[4]; // 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 * pix3[0]; green = f1 * green + f2 * pix3[1]; blue = f1 * blue + f2 * pix3[2]; pix3[0] = red; pix3[1] = green; pix3[2] = blue; } if (sacp_porg) // update window for old overlap area Fpaint3(sacp_porgx,sacp_porgy,sacp_pww,sacp_phh); Fpaint3(sacp_orgx,sacp_orgy,sacpR_ww,sacpR_hh); // 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; 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 if (opac == 128) sa_pixmap[ii] = 1; // edge pixel 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(); sa_show(1); return; } fotoxx-15.11.1/f.tools.cc0000664000175000017500000042042612616075370013563 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** Fotoxx image edit - Tools menu functions m_index dialog to create/update image index file index_rebuild create/update image index file m_options user options 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_darkbright highlight darkest and brightest pixels m_moncolor monitor color and contrast check m_mongamma monitor gamma check and adjust m_changelang choose GUI language m_untranslated report misting translations m_calibrate_printer printer color calibration 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) #include /**************************************************************************/ // Index Image Files menu function // Manual index request, or missing information, or new install. // Dialog to supply top image directories and thumbnails directory. // Update the corresponding config. files and generate new image index. void m_index(GtkWidget *, cchar *menu) { void index_clickfunc(GtkWidget *widget, int line, int pos); int index_dialog_event(zdialog *zd, cchar *event); zdialog *zd; FILE *fid; char filespec[200], buff[200], thumbdirk[200]; char *pp; GtkWidget *widget; int ftf, 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("Select directory for thumbnails."); cchar *termmess = ZTX("Index function terminated. Fotoxx will close. \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; /*** _____________________________________________________ | Index Image Files | // 15.06 | | | Select directories containing image files. | | (subdirectories are included automatically). | | [Select] Select to add, click on X to delete. | | _________________________________________________ | | | X /home//Pictures | | | | X /home//... | | | | | | | | | | | | | | | |_________________________________________________| | | | | [Select] Select directory for thumbnails. | | [_________________________________________________] | | | | [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"); // browsetop button zdialog_add_widget(zd,"label","labgreet2","hbtop",greet2); 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=5"); 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,"text","thumbdirk","frthumb"); // thumbdirk text widget = zdialog_widget(zd,"topdirks"); // set click function for top directories textwidget_set_clickfunc(widget,index_clickfunc); // text window snprintf(thumbdirk,200,"%s/thumbnails",get_zuserdir()); // stuff default thumbnails directory zdialog_stuff(zd,"thumbdirk",thumbdirk); // /home//.fotoxx/thumbnails snprintf(filespec,200,"%s/top_directories",index_dirk); // read top image directories file, widget = zdialog_widget(zd,"topdirks"); // stuff data into dialog widgets fid = fopen(filespec,"r"); if (fid) { zdialog_stuff(zd,"topdirks",""); // clear text list while (true) { pp = fgets_trim(buff,200,fid,1); if (! pp) break; if (strmatchN(buff,"thumbnails directory: /",23)) { // if "thumbnails directory: /..." zdialog_stuff(zd,"thumbdirk",buff+22); // stuff thumbnails directory break; // last record } else wprintf(widget," X %s\n",buff); // stuff " X /dir1/dir2..." } fclose(fid); } else wprintf(widget," X %s/Pictures\n",getenv("HOME")); // default /home//Pictures 15.07 zdialog_resize(zd,400,350); // run dialog zdialog_run(zd,index_dialog_event,"parent"); zstat = zdialog_wait(zd); // wait for completion if (zstat != 2) { // canceled zdialog_free(zd); Fblock = 0; if (! FindexOK && ! Fnoindex) { // index started and not finished zmessageACK(Mwin,termmess); // cannot continue m_quit(0,0); } return; } snprintf(filespec,200,"%s/top_directories",index_dirk); // open/write top directories file fid = fopen(filespec,"w"); if (! fid) { // fatal bug zmessageACK(Mwin,"top directories file: \n %s",strerror(errno)); m_quit(0,0); } widget = zdialog_widget(zd,"topdirks"); // get top directories from dialog widget ftf = 1; while (true) { pp = wscanf(widget,ftf); // loop widget text lines if (! 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 output file } zdialog_fetch(zd,"thumbdirk",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 '/' if (cc > 10) fprintf(fid,"thumbnails directory: %s\n",buff); // write thumbnails directory to output file fclose(fid); zdialog_free(zd); // close dialog index_rebuild(1); // build image index and thumbnail files if (! FindexOK) m_index(0,"retry"); // failed Fblock = 0; return; } // mouse click function for top directories text window // remove directory from list where "X" is clicked void index_clickfunc(GtkWidget *widget, int line, int pos) // 15.07 { GtkTextBuffer *textbuff; GdkWindow *gdkwin; char *pp; char *dirlist[maxtopdirks]; int ftf = 1, ii, jj; gdkwin = gtk_widget_get_window(widget); gdk_window_freeze_updates(gdkwin); for (ii = jj = 0; ii < maxtopdirks; ii++) // loop text lines in widget { // " X /dir1/dir2/... " pp = wscanf(widget,ftf); if (! pp) break; if (ii == line && pos < 3) continue; // if "X" clicked, skip deleted line dirlist[jj] = zstrdup(pp); jj++; } textbuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); // clear widget text gtk_text_buffer_set_text(textbuff,"",0); for (ii = 0; ii < jj; ii++) // stuff remaining lines back into widget { wprintf(widget,"%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) { GtkWidget *widget; char **flist, *thumbdirk; int ii; cchar *topmess = ZTX("Choose top image directories"); cchar *thumbmess = ZTX("Choose thumbnail directory"); if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 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++) { wprintf(widget," X %s\n",flist[ii]); // " X /dir1/dir2/..." zfree(flist[ii]); } zfree(flist); } if (strmatch(event,"browsethumb")) { // [browse] thumbnail directory thumbdirk = zgetfile(thumbmess,MWIN,"folder",getenv("HOME")); if (! thumbdirk) return 1; thumbdirk = zstrdup(thumbdirk,12); if (! strstr(thumbdirk,"/thumbnails")) // if not containing /thumbnails, strcat(thumbdirk,"/thumbnails"); // append /thumbnails zdialog_stuff(zd,"thumbdirk",thumbdirk); zfree(thumbdirk); } 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 top directories list. // Called from main() when Fotoxx is started (menu = 0) // Called from menu function m_index() (menu = 1) zdialog *zd_indexlog; void index_rebuild(int menu) { int indexlog_dialog_event(zdialog *zd, cchar *event); int index_compare(cchar *rec1, cchar *rec2); GtkWidget *wlog; FILE *fid; int ii, jj, ntop, nthumb, err; int ftf, cc, NF, orec, nrec, comp; char *pp, filespec[200]; char buff[XFCC+500]; char **flist, *file, *thumbfile; char *Fupdate = 0; STATB statdat; sxrec_t *sxrec_old = 0, *sxrec_new = 0; int Nold, Nnew, imagesfound, updatesreq; double startime; cchar *exifkeys[11] = { "FileName", 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_latitude_key, exif_longitude_key }; char *ppv[11], *filename; char *exifdate, *iptctags, *iptcrating; char *exifsize, *iptccapt, *exifcomms; char *exifcity, *exifcountry, *exiflat, *exiflong; char city2[100], country2[100], lat2[20], long2[20], gtags2[200]; int indexupdates, thumbupdates, thumbdeletes, sumupdates; char statsmess[100] = "index updates: %d thumbnails: %d deleted: %d \n"; cchar *indexmess = ZTX("No image file index was found.\n" // 15.06 "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 FindexOK = 0; // must run to completion Fblock = 1; // get current top image directories and thumbnails directory // from /home//.fotoxx/image_index/top_directories ntop = nthumb = 0; snprintf(filespec,200,"%s/top_directories",index_dirk); // read top directories 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 directory: /",23)) { if (thumbdirk) zfree(thumbdirk); thumbdirk = zstrdup(buff+22); nthumb = 1; break; } if (topdirks[ntop]) zfree(topdirks[ntop]); topdirks[ntop] = zstrdup(buff); // top directories list in global space if (++ntop == maxtopdirks) break; } fclose(fid); } Ntopdirks = ntop; if (! ntop) { // if nothing found, must ask user zmessageACK(Mwin,indexmess); goto retry; } for (ii = 0; ii < ntop; ii++) { // validate top directories err = stat(topdirks[ii],&statdat); if (err || ! S_ISDIR(statdat.st_mode)) { zmessageACK(Mwin,indexerr,topdirks[ii]); // 15.07 goto retry; } } if (! nthumb) { // no thumbnails directory, must ask user zmessageACK(Mwin,ZTX("no thumbnails directory defined")); goto retry; } cc = strlen(thumbdirk) - 11 ; // check /thumbnails name 15.07 if (! strmatch(thumbdirk+cc,"/thumbnails")) { zmessageACK(Mwin,thumberr,thumbdirk); goto retry; } err = stat(thumbdirk,&statdat); // create thumbnails directory if needed if (err || ! S_ISDIR(statdat.st_mode)) err = mkdir(thumbdirk,0750); if (err) { zmessageACK(Mwin,"%s \n %s",thumbdirk,strerror(errno)); goto retry; } for (ii = 0; ii < ntop; ii++) { // disallow top dir = thumbnail dir 15.06 if (strmatch(topdirks[ii],thumbdirk)) { zmessageACK(Mwin,indexerr,topdirks[ii]); // 15.07 goto retry; } } for (ii = 0; ii < ntop; ii++) // check for duplicate directories 15.07 for (jj = ii+1; jj < ntop; jj++) { if (strmatch(topdirks[ii],topdirks[jj])) { zmessageACK(Mwin,duperr,topdirks[jj]); goto retry; } } // if index update is not wanted, exit now (-noindex parameter) if (Fnoindex && menu == 0) { // repositioned 15.11 printz("metadata index update suppressed \n"); Fblock = 0; return; } // create log window for reporting status and statistics if (zd_indexlog) zdialog_free(zd_indexlog); // make dialog for output log zd_indexlog = zdialog_new(0,Mwin,BOK,Bcancel,null); zdialog_set_decorated(zd_indexlog,0); ///zdialog_set_modal(zd_indexlog); // modal not needed 15.11 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_resize(zd_indexlog,500,300); zdialog_run(zd_indexlog,indexlog_dialog_event,"parent"); wprintf(wlog,"top image directories:\n"); // log top image directories for (ii = 0; ii < Ntopdirks; ii++) wprintf(wlog," %s\n",topdirks[ii]); wprintf(wlog,"thumbnails directory: \n %s \n",thumbdirk); // and thumbnails directory zmainloop(); // read image index file and build "old list" of index recs cc = maximages * sizeof(sxrec_t); sxrec_old = (sxrec_t *) zmalloc(cc); // "old" image index recs Nold = 0; ftf = 1; while (true) { err = read_sxrec_seq(sxrec_old[Nold],ftf); // read curr. index recs if (err) break; if (++Nold == maximages) { printz("exceeded max. images: %d \n",maximages); m_quit(0,0); } } // find all image files and create "new list" of index recs cc = maximages * sizeof(sxrec_t); sxrec_new = (sxrec_t *) zmalloc(cc); // "new" image index recs Fupdate = (char *) zmalloc(maximages); // flag, index update needed Nnew = 0; for (ii = 0; ii < Ntopdirks; ii++) { err = find_imagefiles(topdirks[ii],flist,NF,0); if (err) { printz("find_imagefiles() failure \n"); m_quit(0,0); } if (Nnew + NF > maximages) { printz("exceeded max. images: %d \n",maximages); m_quit(0,0); } for (jj = 0; jj < NF; jj++) { file = flist[jj]; nrec = Nnew++; sxrec_new[nrec].file = zstrdup(file); // image filespec stat(file,&statdat); compact_time(statdat.st_mtime,sxrec_new[nrec].fdate); // file mod date sxrec_new[nrec].pdate[0] = 0; // image date = empty strcpy(sxrec_new[nrec].rating,"0"); // stars = "0" strcpy(sxrec_new[nrec].size,"0x0"); // size = "0x0" sxrec_new[nrec].tags = 0; // tags = empty sxrec_new[nrec].capt = 0; // caption = empty sxrec_new[nrec].comms = 0; // comments = empty sxrec_new[nrec].gtags = 0; // geotags = empty zfree(file); } if (NF) zfree(flist); if (! zd_indexlog) goto retry; // check for user abort } imagesfound = Nnew; // total images found wprintf(wlog,"image files found: %d \n",imagesfound); printz("image files found: %d \n",imagesfound); // 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 if (Nold) HeapSort((char *) sxrec_old,sizeof(sxrec_t),Nold,index_compare); if (Nnew) HeapSort((char *) sxrec_new,sizeof(sxrec_t),Nnew,index_compare); updatesreq = imagesfound; for (orec = nrec = 0; nrec < Nnew; nrec++) // loop all image files { Fupdate[nrec] = 1; // assume index update is needed if (orec == Nold) continue; // no more old recs while (true) { if (orec == Nold) break; comp = strcasecmp(sxrec_old[orec].file,sxrec_new[nrec].file); // compare orec file to nrec file if (comp == 0) comp = strcmp(sxrec_old[orec].file,sxrec_new[nrec].file); if (comp >= 0) break; // orec >= nrec orec++; // orec < nrec, next orec continue; } if (comp == 0) // orec = nrec (same image file) { if (strmatch(sxrec_new[nrec].fdate,sxrec_old[orec].fdate)) // file dates match { Fupdate[nrec] = 0; // index update not needed updatesreq--; strncpy0(sxrec_new[nrec].pdate,sxrec_old[orec].pdate,15); // copy data from old to new strncpy0(sxrec_new[nrec].rating,sxrec_old[orec].rating,2); strncpy0(sxrec_new[nrec].size,sxrec_old[orec].size,15); sxrec_new[nrec].tags = sxrec_old[orec].tags; sxrec_old[orec].tags = 0; sxrec_new[nrec].capt = sxrec_old[orec].capt; sxrec_old[orec].capt = 0; sxrec_new[nrec].comms = sxrec_old[orec].comms; sxrec_old[orec].comms = 0; sxrec_new[nrec].gtags = sxrec_old[orec].gtags; sxrec_old[orec].gtags = 0; } orec++; // next orec } } wprintf(wlog,"index updates needed: %d \n",updatesreq); for (orec = 0; orec < Nold; orec++) // release old list memory { zfree(sxrec_old[orec].file); if (sxrec_old[orec].tags) zfree(sxrec_old[orec].tags); if (sxrec_old[orec].capt) zfree(sxrec_old[orec].capt); if (sxrec_old[orec].comms) zfree(sxrec_old[orec].comms); if (sxrec_old[orec].gtags) zfree(sxrec_old[orec].gtags); } zfree(sxrec_old); sxrec_old = 0; if (! zd_indexlog) goto retry; // check for user abort // Process entries needing update in new index list // (new files or files dated later than image index date). // Get updated metadata from image file EXIF/IPTC data. wprintf(wlog,"updating image index and thumbnails ... \n"); indexupdates = thumbupdates = thumbdeletes = sumupdates = 0; for (nrec = 0; nrec < Nnew; nrec++) // loop all image files { file = sxrec_new[nrec].file; // image file if (! Fupdate[nrec]) goto thumbupdate; // no update needed err = exif_get(file,exifkeys,ppv,11); // get iptc/exif metadata if (err) { printz("exif_get() failure: %s \n",file); // metadata unreadable goto thumbupdate; } pp = strrchr(file,'/'); if (! ppv[0] || ! strmatch(ppv[0],pp+1)) { // image file has no metadata printz("exif_get() no data: %s \n",file); goto thumbupdate; } filename = ppv[0]; exifdate = ppv[1]; iptctags = ppv[2]; 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,sxrec_new[nrec].pdate); else strcpy(sxrec_new[nrec].pdate,"null"); // not present if (iptcrating && strlen(iptcrating)) { // iptc rating sxrec_new[nrec].rating[0] = *iptcrating; sxrec_new[nrec].rating[1] = 0; } else strcpy(sxrec_new[nrec].rating,"0"); // not present if (exifsize && strlen(exifsize)) // exif size strncpy0(sxrec_new[nrec].size,exifsize,15); if (iptctags && strlen(iptctags)) { // iptc tags sxrec_new[nrec].tags = iptctags; iptctags = 0; } if (iptccapt && strlen(iptccapt)) { // iptc caption sxrec_new[nrec].capt = iptccapt; iptccapt = 0; } if (exifcomms && strlen(exifcomms)) { // exif comments sxrec_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); *gtags2 = 0; strncatv(gtags2,200,city2,"^ ",country2,"^ ",lat2,"^ ",long2,null); sxrec_new[nrec].gtags = zstrdup(gtags2); zfree(filename); if (exifdate) zfree(exifdate); // free EXIF data if (iptctags) zfree(iptctags); if (iptcrating) zfree(iptcrating); if (exifsize) zfree(exifsize); if (exifcomms) zfree(exifcomms); if (iptccapt) zfree(iptccapt); if (exifcity) zfree(exifcity); if (exifcountry) zfree(exifcountry); if (exiflat) zfree(exiflat); if (exiflong) zfree(exiflong); indexupdates++; // count index updates thumbupdate: // back to combined processing 15.05 thumbfile = image_thumbfile(file,&ii); // update thumbnail if missing or stale if (thumbfile) zfree(thumbfile); if (ii) thumbupdates++; // count thumbnail updates if (! zd_indexlog) goto retry; // check for user abort if (indexupdates + thumbupdates > sumupdates) // update progress counters wprintf(wlog,-2,statsmess,indexupdates,thumbupdates,thumbdeletes); sumupdates = indexupdates + thumbupdates; } // write updated index records to index file if (indexupdates) // 15.08 { ftf = 1; for (nrec = 0; nrec < Nnew; nrec++) { err = write_sxrec_seq(&sxrec_new[nrec],ftf); if (err) zappcrash("update image index: %s",strerror(errno)); // 15.03 } write_sxrec_seq(null,ftf); // close output } // find orphan thumbnails and delete them snprintf(command,CCC,"find %s -type f",thumbdirk); fid = popen(command,"r"); if (! fid) zappcrash("find command: %s",strerror(errno)); // 15.03 while (true) { thumbfile = fgets_trim(buff,XFCC,fid); if (! thumbfile) break; file = thumb2imagefile(thumbfile); if (file) { zfree(file); continue; } remove(thumbfile); thumbdeletes++; } pclose(fid); // free "new list" memory for (nrec = 0; nrec < Nnew; nrec++) { zfree(sxrec_new[nrec].file); if (sxrec_new[nrec].tags) zfree(sxrec_new[nrec].tags); if (sxrec_new[nrec].capt) zfree(sxrec_new[nrec].capt); if (sxrec_new[nrec].comms) zfree(sxrec_new[nrec].comms); if (sxrec_new[nrec].gtags) zfree(sxrec_new[nrec].gtags); } zfree(sxrec_new); zfree(Fupdate); sxrec_new = 0; Fupdate = 0; printz(statsmess,indexupdates,thumbupdates,thumbdeletes); wprintf(wlog,-2,statsmess,indexupdates,thumbupdates,thumbdeletes); wprintf(wlog,"%s\n",ZTX("COMPLETED")); // index complete and OK FindexOK = 1; // completed OK Fnoindex = 0; // -noindex option moot Fblock = 0; printz("index time: %.1f seconds \n",get_seconds() - startime); // log elapsed time 15.08 for (ii = 0; ii < 5; ii++) { // push last message out and zmainloop(); // delay a bit before killing zsleep(0.1); // the log window } if (! menu && zd_indexlog) { // kill log window if not manual run if (indexupdates || thumbupdates || thumbdeletes) zsleep(1); zdialog_send_event(zd_indexlog,"kill"); } return; // error or user cancel, start over (user can try again or quit) // FindexOK remains = 0 retry: if (zd_indexlog) zdialog_send_event(zd_indexlog,"kill"); if (sxrec_old) zfree(sxrec_old); if (sxrec_new) zfree(sxrec_new); if (Fupdate) zfree(Fupdate); Fblock = 0; m_index(0,"retry"); return; } // index log window dialog response function int indexlog_dialog_event(zdialog *zd, cchar *event) { int nn; cchar *canmess = ZTX("Indexing is required for first-time startup."); if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"kill")) { // auto-kill from index_rebuild() zdialog_free(zd); zd_indexlog = 0; return 1; } if (zd->zstat == 1) { // [OK] button if (FindexOK) { zdialog_free(zd); // index completed, kill dialog zd_indexlog = 0; return 1; } } if (zd->zstat == 2) { // [Cancel] button if (FindexOK) { zdialog_free(zd); // index completed, kill dialog zd_indexlog = 0; return 1; } nn = zdialog_choose(Mwin,canmess,Bcontinue,Bcancel,null); // ask for confirmation 15.07 if (nn == 1) { zd->zstat = 0; // continue return 1; } zdialog_free(zd); // cancel zd_indexlog = 0; return 1; } zd->zstat = 0; // keep dialog open while index busy return 1; } // sort compare function - compare index record filespecs and return // <0 | 0 | >0 for file1 < | == | > file2 int index_compare(cchar *rec1, cchar *rec2) { char * file1 = ((sxrec_t *) rec1)->file; char * file2 = ((sxrec_t *) rec2)->file; int nn = strcasecmp(file1,file2); // compare ignoring case if (nn) return nn; nn = strcmp(file1,file2); // if equal, use utf8 compare return nn; } /**************************************************************************/ // user options dialog namespace useroptions { int options_fclone; cchar *startopt[7][2] = { "recent", ZTX("Recent Files Gallery"), // fotoxx startup display options "newest", ZTX("Newest Files Gallery"), "prevG", ZTX("Previous Gallery"), "prevI", ZTX("Previous Image"), "blank", ZTX("Blank Window"), "dirk", ZTX("Directory"), "file", ZTX("Image File") }; } // menu function void m_options(GtkWidget *, cchar *) { using namespace useroptions; int options_dialog_event(zdialog *zd, cchar *event); int ii; zdialog *zd; char thumbsize[8]; /*** ____________________________________________________________________ | User Options | | | | Startup Display [ previous image |v] | | [Browse] [______________________________________________________] | | | | Menu Style (o) Icons (o) Icons + Text Icon Size [___|-+] | | Dialog font [sans 10 _________] [choose] | | Image Pan/Scroll: [drag magnified |v] Zooms for 2x [___|-+] | | JPEG save quality [____|-+] Thumbnail size [___|v] | | Curve node capture distance [____|-+] | | [x] show hidden directories in gallery view | | [x] prev/next shows last file version only | | RAW command [_________________________________________________] | | RAW file types [_______________________________________________] | | | | [done] | |____________________________________________________________________| ***/ F1_help_topic = "user_options"; if (checkpend("all")) return; // check nothing pending Fblock = 1; zd = zdialog_new(ZTX("User Options"),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 < 7; ii++) zdialog_cb_app(zd,"startopt",startopt[ii][1]); // startup display option 15.07 zdialog_add_widget(zd,"hbox","hbbrowse","dialog","space=3"); zdialog_add_widget(zd,"button","browse","hbbrowse",Bbrowse,"space=5"); zdialog_add_widget(zd,"entry","browsefile","hbbrowse",0,"expand"); zdialog_add_widget(zd,"hbox","hbmenu","dialog"); 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,"spin","iconsize","hbmenu","32|48|1|40","space=3"); zdialog_add_widget(zd,"hbox","hbfont","dialog"); // screen font 15.09 zdialog_add_widget(zd,"label","labfont","hbfont",ZTX("Dialog font and size"),"space=5"); zdialog_add_widget(zd,"entry","font","hbfont","Sans 10","size=20"); zdialog_add_widget(zd,"button","choosefont","hbfont",Bchoose,"space=5"); zdialog_add_widget(zd,"hbox","hbips","dialog"); zdialog_add_widget(zd,"label","labips","hbips",ZTX("Image Pan/scroll:"),"space=5"); zdialog_add_widget(zd,"combo","panscroll","hbips",0); zdialog_add_widget(zd,"label","space","hbips",0,"space=10"); zdialog_add_widget(zd,"label","labzoom","hbips",ZTX("Zooms for 2x"),"space=5"); zdialog_add_widget(zd,"spin","zoomcount","hbips","1|4|1|2"); zdialog_cb_app(zd,"panscroll","drag"); // drag/scroll option 15.07 zdialog_cb_app(zd,"panscroll","scroll"); zdialog_cb_app(zd,"panscroll","drag magnified"); zdialog_cb_app(zd,"panscroll","scroll magnified"); zdialog_add_widget(zd,"hbox","hbjpeg","dialog"); zdialog_add_widget(zd,"label","labqual","hbjpeg",ZTX("JPEG save quality"),"space=5"); zdialog_add_widget(zd,"spin","quality","hbjpeg","1|100|1|90"); zdialog_add_widget(zd,"label","space","hbjpeg",0,"space=10"); zdialog_add_widget(zd,"label","labthumb","hbjpeg",ZTX("Thumbnail size"),"space=5"); zdialog_add_widget(zd,"combo","thumbsize","hbjpeg",0,"space=5"); zdialog_add_widget(zd,"hbox","hbcap","dialog"); // 15.10 zdialog_add_widget(zd,"label","labcap","hbcap",ZTX("Curve node capture distance"),"space=5"); zdialog_add_widget(zd,"spin","nodecap","hbcap","3|20|1|5"); zdialog_add_widget(zd,"hbox","hbhidden","dialog"); zdialog_add_widget(zd,"check","showhidden","hbhidden",ZTX("show hidden directories in gallery view"),"space=5"); zdialog_add_widget(zd,"hbox","hblastver","dialog"); zdialog_add_widget(zd,"check","lastver","hblastver",ZTX("prev/next shows last file version only"),"space=5"); zdialog_add_widget(zd,"hbox","hbrawcomm","dialog"); zdialog_add_widget(zd,"label","rawlab1","hbrawcomm",ZTX("RAW command"),"space=5"); zdialog_add_widget(zd,"entry","rawcomm","hbrawcomm","dcraw -w -q 0 -T -6","expand"); zdialog_add_widget(zd,"hbox","hbrawfile","dialog"); zdialog_add_widget(zd,"label","rawlab2","hbrawfile",ZTX("RAW file types"),"space=5"); zdialog_add_widget(zd,"entry","rawtypes","hbrawfile",".raw .rw2","expand"); for (ii = 0; ii < 7; ii++) { if (strmatch(startdisplay,startopt[ii][0])) // set startup display option 15.07 zdialog_stuff(zd,"startopt",startopt[ii][1]); } if (strmatch(startdisplay,"dirk")) // if directory or file option, 15.07 zdialog_stuff(zd,"browsefile",startdirk); // set current directory or file if (strmatch(startdisplay,"file")) zdialog_stuff(zd,"browsefile",startfile); 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); zdialog_stuff(zd,"iconsize",iconsize); // icon size zdialog_stuff(zd,"font",dialog_font); // dialog font 15.09 if (Fdragopt == 1) zdialog_stuff(zd,"panscroll","drag"); // set pan/scroll option 15.07 if (Fdragopt == 2) zdialog_stuff(zd,"panscroll","scroll"); if (Fdragopt == 3) zdialog_stuff(zd,"panscroll","drag magnified"); if (Fdragopt == 4) zdialog_stuff(zd,"panscroll","scroll magnified"); if (zoomcount >= 1 && zoomcount <= 4) // zooms for 2x increase zdialog_stuff(zd,"zoomcount",zoomcount); zdialog_stuff(zd,"quality",jpeg_def_quality); // default jpeg file save quality zdialog_cb_app(zd,"thumbsize","128"); // thumbnail file pixel size 15.02 zdialog_cb_app(zd,"thumbsize","256"); zdialog_cb_app(zd,"thumbsize","512"); snprintf(thumbsize,8,"%d",thumbfilesize); // preload with current value zdialog_stuff(zd,"thumbsize",thumbsize); zdialog_stuff(zd,"nodecap",splcurve_minx); // edit curve min. node distance 15.10 zdialog_stuff(zd,"showhidden",Fshowhidden); // show hidden dirs in gallery view 15.04 zdialog_stuff(zd,"lastver",Flastversion); // prev/next shows last version only zdialog_stuff(zd,"rawcomm",DCrawcommand); // RAW file conversion command zdialog_stuff(zd,"rawtypes",RAWfiletypes); // RAW file types options_fclone = 0; zdialog_run(zd,options_dialog_event); // run dialog and wait for completion zdialog_wait(zd); zdialog_free(zd); Fblock = 0; if (options_fclone) { // start new and exit current fotoxx m_clone(0,0); m_quit(0,0); } return; } // options dialog event function int options_dialog_event(zdialog *zd, cchar *event) { using namespace useroptions; int ii, jj, nn; char *pp, temp[200]; static char browsefile[500] = ""; GtkWidget *font_dialog; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"thumbsize")) { // warn if thumbfilesize is changed 15.02 zdialog_fetch(zd,"thumbsize",nn); if (nn != thumbfilesize) zmessageACK(Mwin,ZTX("Delete present thumbnails to make effective")); } if (strmatch(event,"enter")) zd->zstat = 1; // [done] 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 < 7; ii++) { if (strmatch(temp,startopt[ii][1])) { zfree(startdisplay); startdisplay = zstrdup(startopt[ii][0]); } } zdialog_fetch(zd,"browsefile",browsefile,500); // initial browse location 15.08 if (! *browsefile && topdirks[0]) strncpy0(browsefile,topdirks[0],500); if (strmatch(startdisplay,"dirk")) { pp = zgetfile(ZTX("Select startup directory"),MWIN,"folder",browsefile); if (! pp) return 1; zdialog_stuff(zd,"browsefile",pp); zfree(pp); } if (strmatch(startdisplay,"file")) { pp = zgetfile(ZTX("Select startup image file"),MWIN,"file",browsefile); if (! pp) return 1; zdialog_stuff(zd,"browsefile",pp); zfree(pp); } } if (strmatch(event,"choosefont")) // choose 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; strncpy0(temp,pp,200); g_free(pp); zdialog_stuff(zd,"font",temp); dialog_font = zstrdup(temp); g_object_set(zfuncs::settings,"gtk-font-name",dialog_font,null); } if (zd->zstat == 1) // done, check inputs are valid { zdialog_fetch(zd,"startopt",temp,200); // get startup option for (ii = 0; ii < 7; ii++) { if (strmatch(temp,startopt[ii][1])) { zfree(startdisplay); startdisplay = zstrdup(startopt[ii][0]); } } if (strmatch(startdisplay,"dirk")) { // startup option = directory zdialog_fetch(zd,"browsefile",browsefile,500); // startup directory if (startdirk) zfree(startdirk); startdirk = zstrdup(browsefile); if (image_file_type(startdirk) != 1) { zmessageACK(Mwin,ZTX("startup directory is invalid")); zd->zstat = 0; return 1; } } if (strmatch(startdisplay,"file")) { // startup option = image file zdialog_fetch(zd,"browsefile",browsefile,500); // startup file if (startfile) zfree(startfile); startfile = zstrdup(browsefile); if (image_file_type(startfile) != 2) { 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"); options_fclone++; } } zdialog_fetch(zd,"both",nn); // menu type = icons + text if (nn) { if (! strmatch(menu_style,"both")) { zfree(menu_style); menu_style = zstrdup("both"); options_fclone++; } } zdialog_fetch(zd,"iconsize",nn); // icon size if (nn != iconsize) { iconsize = nn; options_fclone++; } zdialog_fetch(zd,"font",temp,200); // screen font 15.09 if (*temp > ' ') dialog_font = zstrdup(temp); g_object_set(zfuncs::settings,"gtk-font-name",dialog_font,null); zdialog_fetch(zd,"panscroll",temp,200); // get drag/scroll option 15.07 if strmatch(temp,"drag") Fdragopt = 1; if strmatch(temp,"scroll") Fdragopt = 2; if strmatch(temp,"drag magnified") Fdragopt = 3; if strmatch(temp,"scroll magnified") Fdragopt = 4; zdialog_fetch(zd,"zoomcount",zoomcount); // zooms for 2x image, 1-4 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,"thumbsize",thumbfilesize); // thumbnail file pixel size 15.02 zdialog_fetch(zd,"nodecap",splcurve_minx); // edit curve min. node distance 15.10 zdialog_fetch(zd,"showhidden",Fshowhidden); // show hidden dirs in gallery view 15.04 zdialog_fetch(zd,"lastver",Flastversion); // prev/next shows last version only zdialog_fetch(zd,"rawcomm",temp,200); // command for RAW to tiff using DCraw if (DCrawcommand) zfree(DCrawcommand); DCrawcommand = zstrdup(temp); 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 .raw 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; save_params(); // done, save modified parameters return 1; } return 1; } /**************************************************************************/ // edit 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 = 10; // reserved shortcuts (hard coded) cchar *reserved[10] = { "F", "G", "W", "F1", "+", "=", "-", // use upper case for this table "Z", "S", "M" }; // (upper case X is Shift+X) int Neligible = 25; cchar *eligible[25]; // allowed shortcuts cchar *eligible_en[25] = { "Open Image File", "Open Previous File", "Rename Image File", "Save to Disk", "Trash Image File", "Quit Fotoxx", "Show RGB", "Grid Lines", "Magnify Image", "Edit Metadata", "View Metadata (short)", "Edit Geotags", "Select", "Trim/Rotate", "Resize", "Add Text", "Voodoo Enhance", "Retouch Combo", "Tone Mapping", "Brightness Dist.", "Sharpen", "Red Eyes", "Undo", "Redo", "Keyboard Shortcuts" }; } // KB shortcuts menu function void m_KBshortcuts(GtkWidget *, cchar *) { using namespace KBshortcutnames; void KBshorts_clickfunc(GtkWidget *widget, int line, int pos); int KBshorts_keyfunc(GtkWidget *dialog, GdkEventKey *event); int KBshorts_dialog_event(zdialog *zd, cchar *event); int ii, cc; GtkWidget *widget; char textline[100]; /*** _______________________________________________ | Edit KB Shortcuts | |_______________________________________________| | Alt+G Grid Lines | | Alt+U Undo | | Alt+R Redo | | T Trim/Rotate | | ... ... | |_______________________________________________| | | | shortcut key: [ CTRL+G ] [ (dropdown list) ] | | | | [reserved] [delete] [done] [cancel] | |_______________________________________________| ***/ F1_help_topic = "KB_shortcuts"; zd = zdialog_new(ZTX("Edit KB Shortcuts"),Mwin,Breserved,Bdelete,Bdone,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"); 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)")); zdialog_add_widget(zd,"label","space","hbshort",0,"expand"); zdialog_add_widget(zd,"comboE","shortmenu","hbshort",0,"space=5"); widget = zdialog_widget(zd,"shortlist"); textwidget_set_clickfunc(widget,KBshorts_clickfunc); cc = maxshortcuts * sizeof(char *); memset(shortcutkey2,0,cc); memset(shortcutmenu2,0,cc); for (ii = 0; ii < Neligible; ii++) // use translated menu names eligible[ii] = ZTX(eligible_en[ii]); for (ii = 0; ii < Nshortcuts; ii++) { // copy global shortcuts list shortcutkey2[ii] = zstrdup(shortcutkey[ii]); // for subsequent editing shortcutmenu2[ii] = zstrdup(shortcutmenu[ii]); } Nshortcuts2 = Nshortcuts; wclear(widget); for (int ii = 0; ii < Nshortcuts2; ii++) { // show shortcuts list in dialog snprintf(textline,100,"%-20s %s \n",shortcutkey2[ii],shortcutmenu2[ii]); wprintx(widget,0,textline); } for (int ii = 0; ii < Neligible; ii++) // add eligible menu names to zdialog_cb_app(zd,"shortmenu",eligible[ii]); // combobox dropdown list widget = zdialog_widget(zd,"dialog"); G_SIGNAL(widget,"key-press-event",KBshorts_keyfunc,0); G_SIGNAL(widget,"key-release-event",KBshorts_keyfunc,0); zdialog_resize(zd,420,0); zdialog_run(zd,KBshorts_dialog_event,"save"); return; } // mouse click functions to select existing shortcut from list void KBshorts_clickfunc(GtkWidget *widget, int line, int pos) { using namespace KBshortcutnames; char *txline; char shortkey[20]; char shortmenu[60]; txline = textwidget_get_line(widget,line,1); if (! txline) return; strncpy0(shortkey,txline,20); strncpy0(shortmenu,txline+21,60); zfree(txline); strTrim(shortkey); strTrim(shortmenu); zdialog_stuff(zd,"shortkey",shortkey); zdialog_stuff(zd,"shortmenu",shortmenu); return; } // intercept KB key events int KBshorts_keyfunc(GtkWidget *dialog, GdkEventKey *event) { using namespace KBshortcutnames; static int Ctrl = 0, Alt = 0, Shift = 0; int key, type, ii, cc; char keyname[20]; key = event->keyval; type = event->type; if (type == GDK_KEY_PRESS) type = 1; else type = 0; if (key == GDK_KEY_Control_L || key == GDK_KEY_Control_R) { // Ctrl key Ctrl = type; return 1; } if (key == GDK_KEY_Alt_L || key == GDK_KEY_Alt_R) { // Alt key Alt = type; return 1; } if (key == GDK_KEY_Shift_L || key == GDK_KEY_Shift_R) { // Shift key Shift = type; return 1; } if (type) return 1; // wait for key release if (key == GDK_KEY_F1) { // key is F1 (context help) KBstate(event,0); // 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,"shortmenu",""); // clear menu choice return 1; } // dialog event and completion function int KBshorts_dialog_event(zdialog *zd, cchar *event) { using namespace KBshortcutnames; int ii, jj, err; GtkWidget *widget; char textline[100]; char shortkey[20]; char shortmenu[60]; char kbfile[200]; FILE *fid = 0; if (strmatch(event,"escape")) zd->zstat = 4; // escape = cancel 15.07 if (strmatch(event,"shortmenu")) // menu choice made { zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"shortkey",shortkey,20); // get shortcut key and menu zdialog_fetch(zd,"shortmenu",shortmenu,60); // from dialog widgets if (*shortkey <= ' ' || *shortmenu <= ' ') 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 menu in list if (strmatch(shortcutmenu2[ii],shortmenu)) 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; } if (Nshortcuts2 == maxshortcuts) { zmessageACK(Mwin,"exceed %d shortcuts",maxshortcuts); return 1; } ii = Nshortcuts2++; // add new shortcut to end of list shortcutkey2[ii] = zstrdup(shortkey); shortcutmenu2[ii] = zstrdup(shortmenu); widget = zdialog_widget(zd,"shortlist"); wclear(widget); for (ii = 0; ii < Nshortcuts2; ii++) { // show existing shortcuts in dialog snprintf(textline,100,"%-20s %s \n",shortcutkey2[ii],shortcutmenu2[ii]); wprintx(widget,0,textline); } return 1; } if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) // show reserved shortcuts { zd->zstat = 0; // keep dialog active write_popup_text("open","Reserved Shortcuts",300,200,Mwin); write_popup_text("write","F image File view mode"); write_popup_text("write","G Gallery view mode"); write_popup_text("write","W World maps view mode"); write_popup_text("write","F1 User Guide topic for current function"); write_popup_text("write","+ zoom-in"); write_popup_text("write","= zoom-in"); write_popup_text("write","- zoom-out"); write_popup_text("write","Z toggle image full size / fit window"); write_popup_text("write","S sync gallery to current image file"); write_popup_text("write","M magnify image at mouse drag"); write_popup_text("write",""); write_popup_text("write","These may only be used for custom shortcuts"); write_popup_text("write","in combination with Ctrl/Alt/Shift keys."); return 1; } if (zd->zstat == 2) // delete 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"); wclear(widget); for (ii = 0; ii < Nshortcuts2; ii++) { // show existing shortcuts in dialog snprintf(textline,100,"%-20s %s \n",shortcutkey2[ii],shortcutmenu2[ii]); wprintx(widget,0,textline); } zdialog_stuff(zd,"shortkey",""); zdialog_stuff(zd,"shortmenu",""); return 1; } if (zd->zstat == 3) // done - save the shortcuts { zdialog_free(zd); for (ii = 0; ii < Nshortcuts2; ii++) { // convert menunames back to English for (jj = 0; jj < Neligible; jj++) if (strmatch(eligible[jj],shortcutmenu2[ii])) break; if (jj < Neligible) { zfree(shortcutmenu2[ii]); shortcutmenu2[ii] = zstrdup(eligible_en[jj]); } } err = locale_filespec("user","KB-shortcuts",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,"%-20s %s \n",shortcutkey2[ii],shortcutmenu2[ii]); fclose(fid); for (ii = 0; ii < Nshortcuts; ii++) { // clear global shortcuts data if (shortcutkey[ii]) zfree(shortcutkey[ii]); if (shortcutmenu[ii]) zfree(shortcutmenu[ii]); shortcutkey[ii] = shortcutmenu[ii] = 0; } 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; Nshortcuts = 0; err = locale_filespec("user","KB-shortcuts",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)); // menu can be English or translation if (++ii == maxshortcuts) break; } fclose(fid); Nshortcuts = ii; return 0; } /**************************************************************************/ // create or update brightness distribution graph 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; zdialog *zd; if (! Fpxb) return; if (menu && strmatch(menu,"kill")) { if (zdbrdist) zdialog_destroy(zdbrdist); zdbrdist = 0; return; } if (zdbrdist) { // dialog already present gtk_widget_queue_draw(drawwin_dist); // refresh drawing windows return; } if (menu) F1_help_topic = "show_brdist"; 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"); zdbrdist = zd; return; } // dialog event and completion function int show_brdist_dialog_event(zdialog *zd, cchar *event) { using namespace brdist_names; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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 the histogram and the brightness scale underneath 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; if (! resource_lock(Fpaintlock)) return; // 15.02 gdk_window_freeze_updates(gdkwin); 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]); resource_unlock(Fpaintlock); gdk_window_thaw_updates(gdkwin); 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]; } resource_unlock(Fpaintlock); gdk_window_thaw_updates(gdkwin); 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 (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"; 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,"spin","spacex","vb2","10|200|1|50","space=2"); zdialog_add_widget(zd,"spin","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,"spin","spacey","vb4","10|200|1|50"); zdialog_add_widget(zd,"spin","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_run(zd,gridlines_dialog_event); zdialog_wait(zd); zdialog_free(zd); return; } // dialog event function int gridlines_dialog_event(zdialog *zd, cchar *event) { int G = currgrid; if (strmatch(event,"enter")) zd->zstat = 1; // [done] if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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; if (strmatch(menu,ZTX("Line Color"))) F1_help_topic = "line_color"; if (strmatch(menu,ZTX("Area Color"))) F1_help_topic = "area_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 (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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); // 15.09 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 (strmatch(event,"enter")) zd->zstat = 2; // [done] if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 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 { if (! resource_lock(Fpaintlock)) return; // 15.02 E0pxm = PXM_load(curr_file,1); // never edited, resource_unlock(Fpaintlock); if (! E0pxm) return; // create E0 image for my use pxm = E0pxm; } 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; 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); // 15.09 } 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 M to toggle dialog."); /*** __________________________ | Magnify Image | | | | Drag mouse on image. | | Left click to cancel. | | Key M to toggle dialog. | | | | radius [_____|-+] | | X-size [_____|-+] | | | | [cancel] | |__________________________| ***/ if (zdmagnify) { // toggle magnify mode 15.01 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,"spin","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,"spin","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 gtk_window_present(MWIN); // keep focus on main window 15.01 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,"escape")) event = "kill"; // escape = cancel 15.07 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 15.01 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 (FGW != 'F') 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); } 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 = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(cr,pxb2,org2x,org2y); cairo_paint(cr); cairo_destroy(cr); } 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); // create pixbuf with scaled pixels 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 15.08 px2 = rx + drad; py2 = ry + drad; pix2 = pixels2 + py2 * rs2 + px2 * nch2; // magnified pixel 15.08 memcpy(pix2,pix1,3); // normal pixel replaces magnified pixel } gdk_window_freeze_updates(gdkwin); // stop flicker 15.08 cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(cr,pxb2,org2x,org2y); cairo_paint(cr); Dxpos = org2x + drad + 2; // draw circle around magnified area 15.01 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); cairo_destroy(cr); gdk_window_thaw_updates(gdkwin); return; } /**************************************************************************/ // dark_brite menu function // highlight darkest and brightest pixels namespace darkbrite { float darklim = 0; float brightlim = 255; } void m_darkbrite(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"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=3|expand"); zdialog_add_widget(zd,"vbox","vb3","hb1",0,"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); // 15.01 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 (strmatch(event,"enter")) zd->zstat = 1; // [done] if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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_moncolor(GtkWidget *, cchar *) { int moncolor_dialog_event(zdialog *zd, cchar *event); char file[200]; int err; char *savecurrfile = 0; char *savecurrdirk = 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); if (curr_dirk) savecurrdirk = zstrdup(curr_dirk); snprintf(file,200,"%s/images/colorchart.png",get_zdatadir()); // color chart .png file err = f_open(file,0,0,1); if (err) goto restore; 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_run(zd,moncolor_dialog_event,"0/0"); zdialog_wait(zd); // wait for dialog complete zdialog_free(zd); restore: if (savecurrdirk) { curr_dirk = zstrdup(savecurrdirk); zfree(savecurrdirk); } if (savecurrfile) { f_open(savecurrfile); zfree(savecurrfile); } else { curr_file = 0; if (curr_dirk) gallery(curr_dirk,"init",-1); } Fblock = 0; return; } // dialog event and completion function int moncolor_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 return 1; } /**************************************************************************/ // check and adjust monitor gamma void m_mongamma(GtkWidget *, cchar *) { int mongamma_dialog_event(zdialog *zd, cchar *event); int err; char gammachart[200]; zdialog *zd; char *savecurrent = 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) savecurrent = zstrdup(curr_file); snprintf(gammachart,200,"%s/images/gammachart2.png",get_zdatadir()); // gamma chart .png file err = f_open(gammachart); if (err) goto restore; Fzoom = 1; // scale 100% (required) gtk_window_set_title(MWIN,"adjust gamma chart"); 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_run(zd,mongamma_dialog_event); zdialog_wait(zd); // wait for dialog complete zdialog_free(zd); restore: if (savecurrent) { f_open(savecurrent); // back to current file zfree(savecurrent); } Fblock = 0; return; } // dialog event function int mongamma_dialog_event(zdialog *zd, cchar *event) { double gamma; if (strmatch(event,"enter")) zd->zstat = 1; // [done] if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"gamma")) { zdialog_fetch(zd,"gamma",gamma); shell_ack("xgamma -quiet -gamma %.2f",gamma); } return 0; } /**************************************************************************/ // set GUI language void m_changelang(GtkWidget *, cchar *) { int changelang_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int ii, cc, val, zstat; char progexe[300], lang1[8], *pp; cchar *langs[10] = { "en English", // english first "ca Catalan", "de German", "es Spanish", "fr French", "it Italian", "nl Dutch", "pt Portuguese", "ru Russian", 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_run(zd,changelang_dialog_event); // 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],8); 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; } // dialog event and completion function int changelang_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 return 1; } /**************************************************************************/ // report missing translations in a popup window void m_untranslated(GtkWidget *, cchar *) { int ftf = 1, Nmiss = 0; cchar *missing; char text[40]; F1_help_topic = "missing_translations"; if (strmatchN(zfuncs::zlang,"en",2)) { zmessageACK(Mwin,"use a non-English locale"); // 15.04 return; } write_popup_text("open","missing translations",400,200,Mwin); while (true) { missing = ZTX_missing(ftf); if (! missing) break; write_popup_text("write",missing); Nmiss++; } snprintf(text,40,"%d missing translations",Nmiss); write_popup_text("write",text); write_popup_text("top",0); return; } /**************************************************************************/ // printer color calibration tool 15.09 namespace calibprint { int dialog_event(zdialog *zd, cchar *event); void printchart(); void scanchart(); void fixchart(); void processchart(); // parameters for RGB step size of 16: 0 16 32 48 ... 256 (256 --> 255) // NC colors per RGB dimension (17) (counting both 0 and 255) // CS color step size (16) // TS tile size in pixels (50) // ROWS chart rows (85) ROWS x COLS must be >= NC*NC*NC // COLS chart columns (58) #define NC 17 #define CS 16 #define TS 50 #define NC2 (NC*NC) #define NC3 (NC*NC*NC) #define ROWS 85 #define COLS 58 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 Colors | | | | (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); 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 (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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(); zdialog_free(zd); return 1; } zdialog_fetch(zd,"scanchart",nn); if (nn) { scanchart(); zdialog_free(zd); return 1; } zdialog_fetch(zd,"fixchart",nn); if (nn) { fixchart(); zdialog_free(zd); return 1; } zdialog_fetch(zd,"processchart",nn); if (nn) { processchart(); zdialog_free(zd); return 1; } zdialog_fetch(zd,"printimage",nn); if (nn) { print_calibrated(); zdialog_free(zd); 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, textww, texthh, textrs; uint8 *chartpixels, *textpixels, *pix1, *pix2; PIXBUF *chartpxb, *textpxb; 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 15.10 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; } textpxb = text_pixbuf(" TOP ","sans bold",30,Mwin); textww = gdk_pixbuf_get_width(textpxb); // copy "TOP" in top margin so user texthh = gdk_pixbuf_get_height(textpxb); // can know which end is up textrs = gdk_pixbuf_get_rowstride(textpxb); textpixels = gdk_pixbuf_get_pixels(textpxb); for (row = 0; row < texthh; row++) for (col = 0; col < textww; col++) { pix1 = textpixels + row * textrs + col * 3; pix2 = chartpixels + (row+10) * chartrs + (col+20+margin) * 3; // top left corner memcpy(pix2,pix1,3); } g_object_unref(textpxb); 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 the color chart in vertical orientation."); 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" "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 caused by the scanner. \n" "Trim off all margins. Be careful not to cut off \n" "any color tile edges or leave any white margins.")); 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 15.10 { 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 15.10 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[200]; 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; 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); // 15.11 zdialog_run(zd); 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,strerror(errno)); 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; // 15.11 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(1000); } poptext_killnow(); Ffuncbusy = 0; Fblock = 0; // 15.11 snprintf(printfile,200,"%s/printfile.png",get_zuserdir()); // 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; } /**************************************************************************/ // Dump CPU usage, zdialog statistics, memory statistics // counters are reset void m_resources(GtkWidget *, cchar *) { F1_help_topic = "resources"; // 15.07 zmalloc_report(); printz(" thumbnail cache: %.0f MB \n",navi::thumbcache_MB); 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-15.11.1/zfuncs.h0000644000175000017500000013647712616075370013361 0ustar micomico/************************************************************************** zfuncs.h include file for zfuncs functions Copyright 2006-2015 Michael Cornelison source URL: kornelix.com 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ***************************************************************************/ // zfuncs.h version v.6.2 #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 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 v.5.8 void zfree(void *pp); // free() with counter char *zstrdup(cchar *string, int addcc = 0); // strdup() with counter void zmalloc_report(); // print statistics report v.5.9 void printz(cchar *format, ...); // printf() with immediate fflush() v.5.8 void zpopup_message(int secs, cchar *format, ...); // popup message, thread safe void zbacktrace(); // produce a backtrace to stdout v.5.9 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 fileDT, char *compactDT); // time_t date/time to yyyymmddhhmmss 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 v.6.1 int disktemp(char *disk); // get disk temp, e.g. "/dev/sda" v.5.9 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 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 // measure CPU time spent in a function or code block within a function extern volatile double cpu_profile_timer; // internal data tables extern volatile double cpu_profile_table[100]; extern volatile 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 for output 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. // 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 * SearchWildIgnoreCase(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 order 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 // 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 ============= v.5.7 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); // initz. app directories and files v.4.1 cchar * get_zprefix(); // get /usr or /usr/local ... v.4.1 cchar * get_zuserdir(); // get /home/user/.appname/ cchar * get_zdatadir(); // get data directory cchar * get_zdocdir(); // get document directory cchar * get_zicondir(); // get icon directory v.5.7 int locale_filespec(cchar *ftype, cchar *fname, char *filespec); // get a locale dependent file v.5.5 void showz_userguide(cchar *context = 0); // show user guide in new process void showz_logfile(); // show log file in popup window v.5.2 void showz_textfile(cchar *type, cchar *file); // show text file [.gz] in popup window void showz_html(cchar *url); // show html via preferred browser void zmake_menu_launcher(cchar *command, cchar *cats, cchar *generic); // add desktop menu and launcher v.4.1 /**************************************************************************/ // 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 zthreadcrash(); // crash if thread is not main() thread void get_hardware_info(GtkWidget *); // get hardware info (display, screen, mouse) int move_pointer(GtkWidget *, int px, int py); // move the mouse pointer to px, py // text window print and read utilities void wprintx(GtkWidget *Win, int line, cchar *mess, int bold = 0); // write text to line, optional bold 6.2 void wprintf(GtkWidget *Win, int line, cchar *format, ...); // "printf" version void wprintf(GtkWidget *Win, cchar *format, ... ); // "printf" to next line, scroll up void wscroll(GtkWidget *mLog, int line); // scroll window to put line on screen void wclear(GtkWidget *Win); // clear window void wclear(GtkWidget *Win, int line); // clear from line to end char * wscanf(GtkWidget *Win, int &ftf); // get text lines from edit widget int wfiledump(GtkWidget *Win, char *filespec); // dump text window to file void wfilesave(GtkWidget *Win, GtkWindow *parent); // wfiledump() via file-chooser dialog void wprintp(GtkWidget *Win); // print text window to default printer // intercept text window mouse click functions typedef void clickfunc_t(GtkWidget *widget, int line, int pos); // function to get clicked text position void textwidget_set_clickfunc(GtkWidget *widget, clickfunc_t clickfunc); // (wrapped lines are one logical line) char * textwidget_get_line(GtkWidget *widget, int line, int hilite); // get entire line at clicked position char * textwidget_get_word(char *line, int pos, cchar *dlims, char &end); // get delimited word at clicked position // 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 // v.5.5 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 GtkWidget *layout; // drawing window int xmax, ymax; // layout size v.5.8 int mcount; // menu entry count vmenuent menu[100]; }; Vmenu *Vmenu_new(GtkWidget *vbox); // 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() /**************************************************************************/ // functions to implement GTK dialogs with less complexity // widget types: dialog, hbox, vbox, hsep, vsep, frame, scrwin, label, entry, edit, radio, // check, button, togbutt, spin, combo, comboE, hscale, vscale, colorbutt #define zdmaxwidgets 300 // v.5.8 #define zdmaxbutts 10 #define zdsentinel 0x97530000 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 stopKB; // stop propagation of KB events to parent int rescale; // widget is rescaled for more resolution v.6.0 float lval, nval, hval; // scale range and neutral value GtkWidget *widget; // GTK widget pointer }; struct zdialog { int sentinel1, sentinel2; // validity sentinels void *eventCB; // widget event user callback function 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 int stopKB; // flag, next KB event will be ignored GtkWidget *parent; // parent window or null cchar *compbutton[zdmaxbutts]; // dialog completion button labels v.5.9 GtkWidget *compwidget[zdmaxbutts]; // dialog completion button widgets zwidget widget[zdmaxwidgets]; // dialog widgets (EOF = type = 0) }; zdialog *zdialog_new(cchar *title, GtkWidget *parent, ...); // create a zdialog with opt. buttons void zdialog_set_title(zdialog *zd, cchar *title); // change zdialog title v.6.0 void zdialog_set_modal(zdialog *zd); // set zdialog modal v.6.1 void zdialog_set_decorated(zdialog *zd, int decorated); // set zdialog decorated or not v.6.1 void zdialog_present(zdialog *zd); // zdialog visible and on top v.6.1 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_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 v.6.0 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 KBstate(GdkEventKey *event, int state); // 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 v.5.2 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_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 // write text to popup window, shell command to popup window GtkWidget * write_popup_text(cchar *action, cchar *text = 0, int ww = 0, int hh = 0, GtkWidget *parent = 0); 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 void zmessLogACK(GtkWidget *parent, cchar *format, ... ); // same, with log to STDOUT 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 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(cchar *text, int dx, int dy, float s1, float s2); // show popup text at mouse posn 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 void drag_drop_func(int x, int y, char *text); // user function, get drag_drop text void drag_drop_connect(GtkWidget *window, drag_drop_func); // connect 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 /************************************************************************** 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-15.11.1/fotoxx-15.11.1.cc0000664000175000017500000052225312616075370014331 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** 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 gtimefunc periodic function update_Fpanel update status parameters on F window top panel 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) mouse_event mouse event response function mouse_convert convert mouse/window space to image space m_zoom main window zoom in/out function KBstate send KB key from dialog to main window KBpress KB key press event function KBrelease KB key release 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_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] = ""; int Fclone=0, cloxx=0, cloyy=0, cloww=0, clohh=0; int err; STATB statb; if (argc > 1 && strmatchV(argv[1],"-ver","-v",0)) { printz(Frelease "\n"); // print version and exit return 0; } gtk_init(&argc,&argv); // initz. GTK & strip GTK params zinitapp("fotoxx"); // initz. app directories err = chdir(get_zuserdir()); // set to /home//.fotoxx // initialize externals to default values // (saved parameters will override these) strcpy(zfuncs::zappname,Frelease); // app name and version Ffirsttime = 1; // first time startup (params override) 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 trimsize[0] = 1600; // default initial image trim size trimsize[1] = 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("both"); // default menu style (icons + text) dialog_font = zstrdup("Sans 9"); // default dialog font 15.09 iconsize = 36; // default icon size splcurve_minx = 5; // default curve node separation % 15.10 startdisplay = zstrdup("prevI"); // start with previous image Fdragopt = 1; // image drag with mouse 15.02 zoomcount = 2; // zooms to reach 2x image size zoomratio = sqrtf(2); // corresp. zoom ratio curr_file = curr_dirk = 0; // no curr. file or directory 15.11 thumbdirk = 0; // no thumbnail directory thumbfilesize = 256; // thumbnail files, default pixels 15.02 jpeg_def_quality = 90; // default .jpeg save quality jpeg_1x_quality = 90; // default for 1-time jpeg quality lens_mm = 35; // pano lens parameter colormapfile = zstrdup("undefined"); // printer calibration color map 15.10 DCrawcommand = zstrdup("dcraw -T -6 -w -q 0 -o 1"); // default RAW to tiff command using DCraw 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 "); 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 bgcolor.red = bgcolor.green = bgcolor.blue = 0.2; // main window background color 15.09 bgcolor.alpha = 1; for (int ii = 0; ii < 100; ii++) Nval[ii] = ii; // static integer values 0-99 // file and directory names in user directory /home//.fotoxx/* snprintf(index_dirk,199,"%s/image_index",get_zuserdir()); // image index directory snprintf(tags_defined_file,199,"%s/tags_defined",get_zuserdir()); // defined tags file snprintf(recentfiles_file,199,"%s/recent_files",get_zuserdir()); // recent files file (index func) snprintf(saved_areas_dirk,199,"%s/saved_areas",get_zuserdir()); // saved areas directory snprintf(albums_dirk,199,"%s/albums",get_zuserdir()); // albums directory snprintf(saved_curves_dirk,199,"%s/saved_curves",get_zuserdir()); // saved curves directory snprintf(writetext_dirk,199,"%s/write_text",get_zuserdir()); // write_text directory snprintf(favorites_dirk,199,"%s/favorites",get_zuserdir()); // favorites directory snprintf(mashup_dirk,199,"%s/mashup",get_zuserdir()); // mashup projects directory locale_filespec("data","quickstart.html",quickstart_file); // quickstart html file snprintf(slideshow_dirk,199,"%s/slideshows",get_zuserdir()); // slide show directory snprintf(pattern_dirk,199,"%s/patterns",get_zuserdir()); // pattern files directory snprintf(retouch_combo_dirk,199,"%s/retouch_combo",get_zuserdir()); // retouch combo settings directory snprintf(custom_kernel_dirk,199,"%s/custom_kernel",get_zuserdir()); // custom kernel files directory snprintf(printer_color_dirk,199,"%s/printer_color",get_zuserdir()); // printer calibration directory 15.10 snprintf(edit_scripts_dirk,199,"%s/edit_scripts",get_zuserdir()); // edit script files directory 15.10 snprintf(maps_dirk,199,"/usr/share/fotoxx-maps/data"); // map files in fotoxx-maps package snprintf(searchresults_file,199,"%s/search_results",get_zuserdir()); // output of image search function 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(writetext_dirk,&statb); if (err) mkdir(writetext_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(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); // following are initialized in initzfunc() below: 15.10 // pattern_dirk retouch_combo_dirk custom_kernel_dirk favorites_dirk load_params(); // restore parameters from last session for (int ii = 1; ii < argc; ii++) // command line parameters { char *pp = argv[ii]; 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 new added 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 (strmatchV(pp,"-menu","-m",0) && argc > ii+1) // -m -menu func starting menu func startmenu = zstrdup(argv[++ii]); else if (strmatch(pp,"-noindex")) // -noindex no index update 15.02 Fnoindex = 1; else if (strmatch(pp,"-cycledesktop") && argc > ii+2) { // -cycledesktop album seconds 15.06 run_cycledesktop(argv[ii+1],argv[ii+2]); // run in background without GUI gtk_main(); return 0; } else initial_file = zstrdup(pp); // must be initial file or directory } ZTXinit(lang); // setup locale and translations setlocale(LC_NUMERIC,"en_US.UTF-8"); // stop comma decimal points build_widgets(); // build window widgets and menus 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 if (! zfuncs::screen || ! zfuncs::mouse) { zpopup_message(0,"GTK/GDK failure to find hardware"); // 15.09 m_quit(0,0); exit(1); } m_viewmode(0,"F"); // set F mode initially zmainloop(); // GTK bug workaround 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 } 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; int ftype, Fdirk = 0; char *pp, *pp2; char procfile[20], buff[200]; char KBshortsU[200], KBshortsI[200]; char tonefile[200], badnews[200]; char metalistfile[200]; double freememory, cachememory; float exifver = 0; FILE *fid; STATB statb; err = chdir(get_zuserdir()); // working directory .../.fotoxx printz(Frelease "\n"); // 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); // bugfix: 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"); // check for xdg-open if (! err) Fxdgopen = 1; err = shell_quiet("which dcraw"); // check for DCraw if (! err) Fdcraw = 1; err = shell_quiet("which ufraw"); // check for UFraw if (! err) Fufraw = 1; err = shell_quiet("which rawtherapee"); // check for Raw Therapee if (! err) Frawtherapee = 1; err = shell_quiet("which brasero"); // check for Brasero if (! err) Fbrasero = 1; err = shell_quiet("which hugin"); // check for pano tools (hugin versions) if (! err) PTtools = 1; if (Fexiftool + Fxdgopen < 2) { // check mandatory dependencies strcpy(badnews,ZTX("Please install missing programs:")); if (! Fexiftool) strcat(badnews,"\n exiftool 8.6 or later"); // refuse to start if any are missing if (! Fxdgopen) strcat(badnews,"\n xdg-utils"); if (! Fdcraw) strcat(badnews,"\n DCraw"); zmessageACK(Mwin,badnews); m_quit(0,0); } if (! Fufraw) printz("UFraw not installed \n"); // optional if (! Frawtherapee) printz("Raw Therapee not installed \n"); if (! Fbrasero) printz("Brasero 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 zmessLogACK(Mwin,"fotoxx new release %s",Frelease); Prelease = zstrdup(Frelease); showz_textfile("doc","changelog"); } Ffirsttime = 0; // reset first time flag // delete fotoxx tempdir files if owner process is no longer running contx = 0; while ((pp = command_output(contx,"find /tmp/fotoxx-*",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,200,"/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,200); printz("tempdir: %s \n",tempdir); // file name template for undo/redo files snprintf(URS_filename,200,"%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 // update KB shortcuts if needed err = locale_filespec("user","KB-shortcuts",KBshortsU); // user KB shortcuts if (err) { // not found locale_filespec("data","KB-shortcuts",KBshortsI); shell_quiet("cp %s %s",KBshortsI,KBshortsU); // copy installed KB shortcuts printz("keyboard shortcuts installed \n"); } KBshortcuts_load(); // load KB shortcuts from file // remove obsolete user locales directory // remove in 2016 FIXME 15.04 char locales_dirk[200]; snprintf(locales_dirk,199,"%s/locales",get_zuserdir()); err = stat(locales_dirk,&statb); if (! err) shell_quiet("rm -R %s",locales_dirk); // remove obsolete newest_files file // remove in 2016 FIXME 15.05 char newfiles_file[200]; snprintf(newfiles_file,199,"%s/newest_files",get_zuserdir()); err = stat(newfiles_file,&statb); if (! err) remove(newfiles_file); // copy default data files to user directories if not already err = stat(pattern_dirk,&statb); // pattern files if (err) { printz("copy default pattern files \n"); mkdir(pattern_dirk,0750); shell_quiet("cp -n %s/patterns/* %s",get_zdatadir(),pattern_dirk); } err = stat(retouch_combo_dirk,&statb); // retouch combo settings if (err) { printz("copy default retouch_combo files \n"); mkdir(retouch_combo_dirk,0750); shell_quiet("cp -n %s/retouch_combo/* %s",get_zdatadir(),retouch_combo_dirk); } err = stat(custom_kernel_dirk,&statb); // custom convolution kernels if (err) { printz("copy default custom_kernel files \n"); mkdir(custom_kernel_dirk,0750); shell_quiet("cp -n %s/custom_kernel/* %s",get_zdatadir(),custom_kernel_dirk); } err = stat(favorites_dirk,&statb); // favorites menu if (err) { printz("copy default favorites menu \n"); mkdir(favorites_dirk,0750); shell_quiet("cp -n %s/favorites/* %s",get_zdatadir(),favorites_dirk); } err = stat(tags_defined_file,&statb); // tags_defined file if (err) { printz("create default tags_defined file \n"); shell_quiet("cp %s/tags_defined %s",get_zdatadir(),tags_defined_file); } snprintf(tonefile,200,"%s/slideshow-tone.oga",slideshow_dirk); // default slide show pause tone err = stat(tonefile,&statb); if (err) shell_quiet("cp %s/slideshow-tone.oga %s",get_zdatadir(),tonefile); snprintf(metalistfile,200,"%s/metadata_short_list",get_zuserdir()); // metadata keys short list 15.10 err = stat(metalistfile,&statb); if (err) shell_quiet("cp %s/metadata_short_list %s",get_zdatadir(),metalistfile); // miscellaneous screen = gtk_window_get_screen(MWIN); // get monitor pixel dimensions screenww = gdk_screen_get_width(screen); screenhh = gdk_screen_get_height(screen); printz("screen width: %d height: %d \n",screenww,screenhh); 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); zdialog_inputs("load"); // load saved dialog inputs zdialog_positions("load"); // load saved dialog positions exif_server(0,0,0); // kill orphan exiftool process g_timeout_add(20,gtimefunc,0); // start periodic function (20 ms) 15.05 // create or update image index file // cannot continue until completed OK index_rebuild(0); // must be successfully completed printz("RAW file types found: %s \n",myRAWtypes); // list RAW types found // set current file and gallery from command line if present if (topdirks[0]) curr_dirk = zstrdup(topdirks[0]); // default 1st top image directory 15.11 else curr_dirk = zstrdup(getenv("HOME")); if (initial_file) { // from command line pp = realpath(initial_file,0); // prepend directory if needed if (pp) { zfree(initial_file); initial_file = zstrdup(pp); free(pp); // not zfree(), bugfix 15.05 } else { printz("invalid file: %s \n",initial_file); // invalid file path zfree(initial_file); initial_file = 0; } } if (initial_file) { ftype = image_file_type(initial_file); if (ftype == 0) { // non-existent file printz("invalid file: %s \n",initial_file); zfree(initial_file); initial_file = 0; } else if (ftype == 1) { // is a directory if (curr_dirk) zfree(curr_dirk); curr_dirk = initial_file; initial_file = 0; Fdirk = 1; } else { if (curr_dirk) zfree(curr_dirk); curr_dirk = zstrdup(initial_file); // set current directory from file pp = strrchr(curr_dirk,'/'); if (pp) *pp = 0; } } if (initial_file) // open initial file f_open(initial_file); else if (Fdirk) { // open initial directory - gallery gallery(curr_dirk,"init"); m_viewmode(0,"G"); gallery(0,"paint",0); } else if (Fprev) { // start with previous file if (last_curr_file && *last_curr_file == '/') // 15.07 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 15.07 curr_file = curr_dirk = 0; navi::galleryname = 0; navi::gallerytype = 0; set_mwin_title(); } // if no command line option, get startup display from user options else if (strmatch(startdisplay,"recent")) // start with recent files gallery m_recentfiles(0,0); else if (strmatch(startdisplay,"newest")) // start with newest files gallery 15.07 m_newfiles(0,"file"); // by file mode date else if (strmatch(startdisplay,"prevG")) { // start with previous gallery 15.07 if (last_gallerytype > 0) { navi::gallerytype = last_gallerytype; if (last_gallerytype == 1) gallery(last_galleryname,"init"); else gallery(last_galleryname,"initF"); m_viewmode(0,"G"); gallery(0,"paint",0); } } else if (strmatch(startdisplay,"prevI")) { // start with previous image file if (last_curr_file && *last_curr_file == '/') f_open(last_curr_file); } else if (strmatch(startdisplay,"dirk")) { // start with given directory if (startdirk && *startdirk == '/') { if (curr_dirk) zfree(curr_dirk); curr_dirk = zstrdup(startdirk); navi::thumbsize = 128; gallery(curr_dirk,"init",0); m_viewmode(0,"G"); gallery(0,"paint",0); } } else if (strmatch(startdisplay,"file")) // start with given image file f_open(startfile); if (startmenu) { // startup menu on command line printz("startmenu: %s \n",startmenu); for (ii = 0; ii < Nmenus; ii++) // convert menu name to menu function if (strmatchcase(ZTX(startmenu),ZTX(menutab[ii].menu))) break; if (ii < Nmenus) menutab[ii].func(0,menutab[ii].arg); // call the menu function } save_params(); // save parameters now 15.07 zmainloop(); // mysterious, avoid blank window return 0; // don't come back } /**************************************************************************/ // functions for main window event signals int delete_event() // main window closed { int yn; printz("main window delete event \n"); if (checkpend("mods")) return 1; // allow user bailout if (zfuncs::zdialog_busy) { yn = zmessageYN(Mwin,ZTX("Kill active dialog?")); // allow user bailout 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 Ffullscreen = 0; return 0; } /**************************************************************************/ // Periodic function (20 milliseconds) int gtimefunc(void *) { void paint_busy(); static int domore = 0; if (Fshutdown) return 0; // shutdown underway if (Fpaintrequest && Cdrawin && ! Fpaintlock) // window update needed gtk_widget_queue_draw(Cdrawin); // (avoid blank screen if blocked) if (zd_thread && zd_thread_event) { // send dialog event from thread zdialog_send_event(zd_thread,zd_thread_event); zd_thread_event = 0; } if (--domore > 0) return 1; // do rest every 200 milliseconds 15.05 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; char text1[300], text2[100]; static char ptext1[300] = ""; double freemem, cachemem; 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; zthreadcrash(); if (ftf) { ftf = 0; reduced = ZTX("(reduced)"); areaactive = ZTX("area active"); dialogopen = ZTX("dialog open"); blocked = ZTX("blocked"); modified = "mod"; } if (FGW != '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; } parseprocfile("/proc/meminfo","MemFree:",&freemem,0); // get amount of free memory parseprocfile("/proc/meminfo","Cached:",&cachemem,0); freemem = (freemem + cachemem) / 1024; // megabytes snprintf(text1,300,"CPU %03.0f%c MB %.0f",cpuload,'%',freemem); // CPU 023% MB 2345 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 || Fgtagmod) // 15.03 strncatv(text1,300," ",ZTX("metadata"),0); } else if (Fpxb) { // 15.03 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 (! 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 bar if Fbusy_goal > 0. // added to top panel: BUSY [.......... ] static GtkWidget *busylabel = 0, *progresslabel = 0; static cchar *busytext = "BUSY"; static char progressdots[24] = "[....................]"; int Ndots = 0; if (Ffuncbusy | Fthreadbusy) { if (! busylabel) { busylabel = gtk_label_new(null); gtk_label_set_markup(GTK_LABEL(busylabel),busytext); gtk_box_pack_start(GTK_BOX(Fpanel),busylabel,0,0,5); } } else { if (busylabel) gtk_widget_destroy(busylabel); busylabel = 0; } if (Fbusy_done > 0 && Fbusy_done < Fbusy_goal) { if (! progresslabel) { progresslabel = gtk_label_new(""); gtk_box_pack_start(GTK_BOX(Fpanel),progresslabel,0,0,0); } Ndots = 20 * Fbusy_done / Fbusy_goal; // 0 - 20 memset(progressdots+1,'.',Ndots); // [ N dots + 20-N blanks ] memset(progressdots+1+Ndots,' ',20-Ndots); gtk_label_set_text(GTK_LABEL(progresslabel),progressdots); } else { if (progresslabel) gtk_widget_destroy(progresslabel); progresslabel = 0; } gtk_widget_show_all(Fpanel); 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; static int pdww = 0, pdhh = 0; // prior window size static int Fimage = 0; float wscale, hscale; int fww, fhh; // current image size at 1x int mww, mhh; // scaled image size int morgx, morgy; int centerx, centery; int refresh = 0; int mousex, mousey; // mouse position after zoom float magx, magy; // mouse drag, magnification ratios if (Fshutdown) return 1; // shutdown underway if (! Cdrawin || ! gdkwin || ! Cstate || ! Cstate->fpxb) { // no image 15.10 if (Fimage) printf("Fpaint, no image \n"); // message only one time Fimage = 0; Fpaintrequest = 0; return 1; } Fimage = 1; 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 (! resource_lock(Fpaintlock)) { // come back later bugfix 15.09.1 Fpaintrequest = 1; return 1; } if (Dww != pdww || Dhh != pdhh) { // window size changed refresh = 1; // image refresh needed pdww = Dww; pdhh = Dhh; } if (Fpaintrequest) { // window image changed 15.09 Fpaintrequest = 0; // window updated as of NOW 15.09 refresh = 1; // image refresh needed if (FGW == 'F' && (E0pxm || E3pxm)) { // insure window F 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 (CEF && dww < Dww) Cstate->dorgx = Dww - dww; // active edit, push image to right 15.11 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 gdk_cairo_set_source_rgba(cr,&bgcolor); // paint background color 15.09 cairo_paint(cr); morgx = Cstate->morgx; // window position in (larger) image morgy = Cstate->morgy; 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,Cstate->dorgx,Cstate->dorgy); // paint section cairo_paint(cr); g_object_unref(pixbuf); mwcr = cr; // cairo context for draw funcs below cairo_set_line_width(mwcr,1); if (Cstate == &Fstate) { // view mode is image if (Ntoplines) draw_toplines(1); // draw line overlays if (gridsettings[currgrid][GON]) draw_gridlines(); // draw grid lines if (Ntoptext) draw_toptext(); // draw text strings if (Ntopcircles) draw_topcircles(); // draw circles if (Fshowarea) sa_show(1); // draw select area outline } if (Cstate == &Wstate) // view mode is world maps geomap_paint_dots(); // 15.01 mwcr = 0; // cairo context invalid resource_unlock(Fpaintlock); // unlock if (refresh && zdbrdist) m_show_brdist(0,0); // update brightness distribution return 1; } /**************************************************************************/ // Repaint modified image synchrounously. // May NOT be called from threads. void Fpaintnow() { if (! Cdrawin || ! gdkwin || ! Cstate || ! Cstate->fpxb) { // no image 15.10 printf("Fpaintnow(), no image \n"); return; } Fpaintrequest = 1; // request repaint of changed image 15.09 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 15.09 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) { 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); // update drawing window from Mpxb 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) { PIXBUF *pixbuf; int px2, py2, ww2, hh2; int crflag = 0; zthreadcrash(); 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) ww2 = Mscale * ww3 + 2 / Mscale + 4; hh2 = Mscale * hh3 + 2 / Mscale + 4; if (px2 + ww2 > Mpxb->ww) ww2 = Mpxb->ww - px2; // avoid overshoot if (py2 + hh2 > Mpxb->hh) hh2 = Mpxb->hh - py2; if (px2 < Morgx) { // reduce to currently visible window ww2 = ww2 - (Morgx - px2); px2 = Morgx; } if (py2 < Morgy) { hh2 = hh2 - (Morgy - py2); py2 = Morgy; } if (ww2 <= 0 || hh2 <= 0) return; // completely outside visible window if (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already crflag = 1; } pixbuf = gdk_pixbuf_new_subpixbuf(Mpxb->pixbuf,px2,py2,ww2,hh2); // Mpxb area to paint 15.08 px2 = px2 - Morgx + Dorgx; // corresp. position in drawing window py2 = py2 - Morgy + Dorgy; if (Mpxb->nc > 3) { // alpha channel present 15.09 cairo_rectangle(mwcr,px2,py2,ww2,hh2); // paint background color gdk_cairo_set_source_rgba(mwcr,&bgcolor); cairo_fill(mwcr); } gdk_cairo_set_source_pixbuf(mwcr,pixbuf,px2,py2); // draw area to window cairo_paint(mwcr); 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); // refresh select area outline } if (crflag) { cairo_destroy(mwcr); mwcr = 0; } return; } /**************************************************************************/ // 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; static int bdtime = 0, butime = 0; static int mdragx0, mdragy0; 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; mouse_convert(Mwxposn,Mwyposn,Mxposn,Myposn); // convert to image space scroll = ((GdkEventScroll *) event)->direction; // scroll wheel event 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 && 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; } 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 if (Mxposn == Mxdown && Myposn == Mydown) { // and not moving 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)(); Fmousemain = 1; // click/drag params are processed here } // unless reset by handler func. if (FGW == 'W') geomap_mousefunc(); // geomap mouse function if (! Fmousemain) return; // curr. function handles mouse 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 && FGW == 'F') image_Rclick_popup(); // image right-click popup menu } if ((Mxdrag || Mydrag) && ! Fpaintlock) // drag = scroll by mouse 15.05 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 out 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; zthreadcrash(); 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 (FGW == '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 KBstate(GdkEventKey *event, int state) { if (state) KBpress(0,event,0); else KBrelease(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); } int KBpress(GtkWidget *win, GdkEventKey *event, void *) // prevent propagation of key-press { // events to menu buttons KBkey = event->keyval; 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; return 1; } int KBrelease(GtkWidget *win, GdkEventKey *event, void *) // some key was released { int ii, jj, cc; char matchkey[20]; 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; KBkey = event->keyval; if (! KBcontrolkey && ! KBshiftkey && ! KBaltkey) { // f/g/w keys set corresp. window mode if (KBkey == GDK_KEY_f) { m_viewmode(0,"F"); goto KBend; } // Ctrl/Alt + f/g/w can be used for shortcuts if (KBkey == GDK_KEY_g) { m_viewmode(0,"G"); goto KBend; } if (KBkey == GDK_KEY_w) { m_viewmode(0,"W"); goto KBend; } } if (KBcapture) return 1; // let current function handle it if (KBcontrolkey + KBshiftkey + KBaltkey) goto KBcustom; // combinations are always custom 15.06 // reserved shortcuts (not user configurable) if (KBkey == GDK_KEY_F1) { // F1 >> user guide showz_userguide(F1_help_topic); // show topic if there, or page 1 goto KBend; } if (KBkey == GDK_KEY_F11) { // F11 is fullscreen toggle if (Fslideshow) { ss_escape = 1; // quit slideshow if active goto KBend; } if (! Ffullscreen) win_fullscreen(0); // toggle full-screen mode and back else win_unfullscreen(); goto KBend; } if (KBkey == GDK_KEY_Escape) { // ESC key if (Ffullscreen) win_unfullscreen(); // exit full screen mode if (Fslideshow) ss_escape = 1; // quit slideshow if active goto KBend; } if (KBkey == GDK_KEY_s) // 's' shortcut for Sync Gallery 15.02 navi::menufuncx(0,"Sync"); if (FGW == 'G') { // window mode = G navi::KBrelease(win,event,0); // pass to gallery KB function goto KBend; // (keys below not for gallery) } if (KBkey == GDK_KEY_z) { // 'z' key if (! KBcontrolkey) m_zoom(0,"100"); // if no CTRL, toggle zoom 1x goto KBend; } if (FGW == 'W') goto KBend; // window mode = W, no other KB events if (KBkey == GDK_KEY_plus) m_zoom(0,"in"); // + key >> zoom in if (KBkey == GDK_KEY_minus) m_zoom(0,"fit"); // - key >> fit to window if (KBkey == GDK_KEY_equal) m_zoom(0,"in"); // = key: same as + if (KBkey == GDK_KEY_KP_Add) m_zoom(0,"in"); // keypad + if (KBkey == GDK_KEY_KP_Subtract) m_zoom(0,"fit"); // keypad - if (Fslideshow) { // slide show mode if (KBkey == GDK_KEY_Left) ss_Larrow = 1; // arrow keys = prev/next image if (KBkey == GDK_KEY_Right) ss_Rarrow = 1; if (KBkey == GDK_KEY_space) ss_spacebar = 1; // spacebar = pause/resume if (KBkey == GDK_KEY_b) ss_Bkey = 1; // 'b' = blank screen + pause if (KBkey == GDK_KEY_m) ss_Mkey = 1; // 'm' shortcut for Magnify goto KBend; } if (Fmashup) { // mashup active, pass KB key mashup::KBfunc(KBkey); goto KBend; } if (CEF && CEF->menufunc == m_trimrotate) { // trim_rotate active, pass KB key trimrotate::KBfunc(KBkey); goto KBend; } if (CEF && CEF->menufunc == m_perspective) { // perspective active, pass KB key perspective::KBfunc(KBkey); goto KBend; } if (KBkey == GDK_KEY_Left) m_prev(0,0); // arrow keys = prev/next image if (KBkey == GDK_KEY_Right) m_next(0,0); if (KBkey == GDK_KEY_Page_Up) m_prev(0,0); // page up/down = prev/next image if (KBkey == GDK_KEY_Page_Down) m_next(0,0); if (KBkey == GDK_KEY_Delete) m_trash(0,0); // delete >> trash if (KBkey == GDK_KEY_m) // 'm' Magnify 15.01 m_magnify(0,0); // look for custom shortcut keys in shortcut table KBcustom: if (KBkey >= GDK_KEY_F1 && KBkey <= GDK_KEY_F9) { // input key is F1 to F9 ii = KBkey - GDK_KEY_F1; strcpy(matchkey,"F1"); matchkey[1] += ii; } else if (KBkey > 255) goto KBend; // not a single letter or digit else { *matchkey = 0; // build input key combination if (KBcontrolkey) strcat(matchkey,"Ctrl+"); // [Ctrl+] [Alt+] [Shift+] key if (KBaltkey) strcat(matchkey,"Alt+"); if (KBshiftkey) strcat(matchkey,"Shift+"); cc = strlen(matchkey); matchkey[cc] = KBkey; matchkey[cc+1] = 0; } for (ii = 0; ii < Nshortcuts; ii++) // convert key combination to menu name if (strmatchcase(matchkey,shortcutkey[ii])) break; if (ii < Nshortcuts) { // convert menu name to menu function for (jj = 0; jj < Nmenus; jj++) { if (! menutab[jj].menu) continue; // ignore separator if (strmatchcase(shortcutmenu[ii],ZTX(menutab[jj].menu))) break; } if (jj < Nmenus) menutab[jj].func(0,menutab[jj].arg); // call the menu function } KBend: KBkey = 0; return 1; } /**************************************************************************/ // set the main window to fullscreen status // (with no menu or panel) void win_fullscreen(int hidemenu) { zthreadcrash(); if (FGW == 'F' && hidemenu) { // if F window, hide menu and panel gtk_widget_hide(Fmenu); gtk_widget_hide(Fpanel); } gtk_window_fullscreen(MWIN); while (! Ffullscreen) zmainloop(); // 15.09 return; } // restore window to former size and restore menu etc. void win_unfullscreen() { zthreadcrash(); gtk_window_unfullscreen(MWIN); // restore old window size gtk_widget_show(Fmenu); gtk_widget_show(Fpanel); Ffullscreen = 0; return; } /**************************************************************************/ // update information in main window title bar void set_mwin_title() { int cc, gtype, fposn, nfiles, nimages; char *pp, titlebar[250]; char pdate[12], ptime[12], pdatetime[20]; char fname[100], gname[100], fdirk[100]; zthreadcrash(); if (! curr_file || *curr_file != '/') { gtk_window_set_title(MWIN,Frelease); 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 = gallery_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_pdate) { metadate_pdate(meta_pdate,pdate,ptime); // get formatted date and time 15.03 strncpy0(pdatetime,pdate,11); // yyyy-mm-dd hh:mm strncpy0(pdatetime+11,ptime,6); pdatetime[10] = ' '; } else strcpy(pdatetime,"(undated)"); gtype = navi::gallerytype; if (gtype == 1) // gallery name = directory snprintf(titlebar,250,"%s %d/%d %s %s %s", Frelease,fposn,nimages,fname,pdatetime,fdirk); else { if (gtype == 2 || gtype == 3) strcpy(gname,"SEARCH RESULTS"); else if (gtype == 4) { pp = strrchr(navi::galleryname,'/'); if (! pp) pp = navi::galleryname; else pp++; strcpy(gname,"ALBUM: "); strncpy0(gname+7,pp,87); } else if (gtype == 5) strcpy(gname,"RECENT FILES"); else if (gtype == 6) strcpy(gname,"NEWEST FILES"); else strcpy(gname,"NO GALLERY"); if (fposn > 0) snprintf(titlebar,250,"%s %s %d/%d %s %s %s", // window title bar Frelease,gname,fposn,nimages,fname,pdatetime,fdirk); else snprintf(titlebar,250,"%s %s (*)/%d %s %s %s", // image not in gallery Frelease,gname,nimages,fname,pdatetime,fdirk); } 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) { int qx, qy, crflag = 0; static int pqx, pqy; static uint8 pixel[12]; // 2x2 block of pixels static PIXBUF *pixbuf1 = 0, *pixbuf4 = 0; zthreadcrash(); 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 15.09 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 (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already crflag = 1; } if (Mscale <= 1) { // write 1x1 pixels 15.08 pixel[0] = LINE_COLOR[0]; pixel[1] = LINE_COLOR[1]; pixel[2] = LINE_COLOR[2]; gdk_cairo_set_source_pixbuf(mwcr,pixbuf1,qx+Dorgx,qy+Dorgy); cairo_paint(mwcr); } 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(mwcr,pixbuf4,qx+Dorgx,qy+Dorgy); cairo_paint(mwcr); } if (crflag) { cairo_destroy(mwcr); mwcr = 0; } return; } // erase one drawn pixel - restore from window image Mpxb. // px, py are image space. void erase_pixel(int px, int py) { GdkPixbuf *pixbuf; static int pqx, pqy; int qx, qy, crflag = 0; zthreadcrash(); 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; if (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already crflag = 1; } pixbuf = gdk_pixbuf_new_subpixbuf(Mpxb->pixbuf,qx,qy,2,2); // 2x2 Mpxb area to copy 15.08 qx = qx - Morgx + Dorgx; // target pixel in window qy = qy - Morgy + Dorgy; gdk_cairo_set_source_pixbuf(mwcr,pixbuf,qx,qy); cairo_paint(mwcr); g_object_unref(pixbuf); if (crflag) { cairo_destroy(mwcr); mwcr = 0; } 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) { float px1, py1, px2, py2; double dashes[2] = { 4, 4 }; int crflag = 0; double R, G, B; zthreadcrash(); 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; if (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already cairo_set_line_width(mwcr,1); crflag = 1; } R = LINE_COLOR[0] / 255.0; // use line color G = LINE_COLOR[1] / 255.0; B = LINE_COLOR[2] / 255.0; cairo_set_source_rgb(mwcr,R,G,B); if (type == 2) cairo_set_dash(mwcr,dashes,2,0); // dotted line else cairo_set_dash(mwcr,dashes,0,0); cairo_move_to(mwcr,px1,py1); // draw line cairo_line_to(mwcr,px2,py2); cairo_stroke(mwcr); if (crflag) { cairo_destroy(mwcr); mwcr = 0; } 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) { float pxm, pym, slope; int crflag = 0; zthreadcrash(); if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already 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); } } else { slope = 1.0 * (y2 - y1) / (x2 - x1); for (pxm = x1; pxm <= x2; pxm++) { pym = y1 + slope * (pxm - x1); erase_pixel(pxm,pym); } } if (crflag) { cairo_destroy(mwcr); mwcr = 0; } 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) { int ii, crflag = 0; zthreadcrash(); if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already cairo_set_line_width(mwcr,1); 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); 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); if (crflag) { cairo_destroy(mwcr); mwcr = 0; } 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() { int crflag = 0, G = currgrid; int px, py, gww, ghh; int startx, starty, endx, endy, stepx, stepy; int startx1, starty1; zthreadcrash(); 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 * (trimrect[2] - trimrect[0]); // fit grid box to trim rectangle ghh = Mscale * (trimrect[3] - trimrect[1]); startx = Mscale * trimrect[0] - Morgx + Dorgx; starty = Mscale * trimrect[1] - 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 (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already cairo_set_line_width(mwcr,1); crflag = 1; } cairo_set_source_rgb(mwcr,1,1,1); // white lines if (gridsettings[G][GX] && stepx) for (px = startx1; px < endx; px += stepx) { cairo_move_to(mwcr,px,starty); cairo_line_to(mwcr,px,endy); } if (gridsettings[G][GY] && stepy) for (py = starty1; py < endy; py += stepy) { cairo_move_to(mwcr,startx,py); cairo_line_to(mwcr,endx,py); } cairo_stroke(mwcr); cairo_set_source_rgb(mwcr,0,0,0); // adjacent black lines if (gridsettings[G][GX] && stepx) for (px = startx1+1; px < endx+1; px += stepx) { cairo_move_to(mwcr,px,starty); cairo_line_to(mwcr,px,endy); } if (gridsettings[G][GY] && stepy) for (py = starty1+1; py < endy+1; py += stepy) { cairo_move_to(mwcr,startx,py); cairo_line_to(mwcr,endx,py); } cairo_stroke(mwcr); if (crflag) { cairo_destroy(mwcr); mwcr = 0; } 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) { zthreadcrash(); 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() { int crflag = 0; zthreadcrash(); if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already cairo_set_line_width(mwcr,1); crflag = 1; } for (int ii = 0; ii < Ntoptext; ii++) draw_text(toptext[ii].px,toptext[ii].py,toptext[ii].text,toptext[ii].font); if (crflag) { cairo_destroy(mwcr); mwcr = 0; } 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) { static PangoFontDescription *pangofont = 0; static PangoLayout *pangolayout = 0; static char priorfont[40] = ""; int ww, hh, crflag = 0; zthreadcrash(); 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 (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already cairo_set_line_width(mwcr,1); crflag = 1; } cairo_set_source_rgb(mwcr,1,1,1); // draw white background cairo_rectangle(mwcr,px,py,ww,hh); cairo_fill(mwcr); cairo_move_to(mwcr,px,py); // draw layout with text cairo_set_source_rgb(mwcr,0,0,0); pango_cairo_show_layout(mwcr,pangolayout); if (crflag) { cairo_destroy(mwcr); mwcr = 0; } 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() { int ii, px, py, rad; int crflag = 0; double R, G, B; zthreadcrash(); if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already cairo_set_line_width(mwcr,1); crflag = 1; } R = LINE_COLOR[0] / 255.0; // use LINE_COLOR G = LINE_COLOR[1] / 255.0; B = LINE_COLOR[2] / 255.0; 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(mwcr,R,G,B); cairo_arc(mwcr,px,py,rad,0,2*PI); // draw 360 deg. arc cairo_stroke(mwcr); } if (crflag) { cairo_destroy(mwcr); mwcr = 0; } 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, cr: center and radius of circle in image space. // if Ferase, then erase previous circle only. void draw_mousecircle(int cx, int cy, int cr, int Ferase) // 15.09 { int px3, py3, ww3, hh3; static int ppx3, ppy3, pww3 = 0, phh3; int crflag = 0; int px, py, qx, qy, nc, rs, pok; uint8 *mpix, *mpix1; double R, G, B; double t, dt, t1, t2; zthreadcrash(); if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->mpxb) return; // no image if (pww3 > 0) { // erase prior Fpaint4(ppx3,ppy3,pww3,phh3); // refresh from Mpxb pww3 = 0; } if (Ferase) return; // erase only, done rs = gdk_pixbuf_get_rowstride(Cstate->mpxb->pixbuf); // image row stride nc = gdk_pixbuf_get_n_channels(Cstate->mpxb->pixbuf); // image no. channels mpix = gdk_pixbuf_get_pixels(Cstate->mpxb->pixbuf); // image pixels px3 = cx - cr - 2; // convert pointer center + radius py3 = cy - cr - 2; // to block position, width, length ww3 = 2 * cr + 4; hh3 = 2 * cr + 4; ppx3 = px3; // remember pixel block area ppy3 = py3; // to erase in next call pww3 = ww3; phh3 = hh3; if (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already crflag = 1; } cx = cx * Mscale - Morgx + Dorgx; // convert to window coordinates cy = cy * Mscale - Morgy + Dorgy; cr = cr * Mscale; R = LINE_COLOR[0] / 255.0; // use LINE_COLOR G = LINE_COLOR[1] / 255.0; B = LINE_COLOR[2] / 255.0; cairo_set_source_rgb(mwcr,R,G,B); t1 = t2 = -1; // angle limits of arc to draw dt = 1.0 / cr; for (t = 0; t < 2*PI; t += dt) // loop 0-360 degrees { px = cx + cr * cos(t); // pixel on mouse circle py = cy + cr * 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 && nc > 3) { qx = px + Morgx - Dorgx; // check alpha channel qy = py + Morgy - Dorgy; mpix1 = mpix + qy * rs + qx * nc; if (mpix1[3] < 128) pok = 0; // pixel is transparent } 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(mwcr,cx,cy,cr,t1,t2); // draw accumulated arc cairo_stroke(mwcr); t1 = t2 = -1; // start over } } if (t1 >= 0) { cairo_arc(mwcr,cx,cy,cr,t1,t2); // draw rest of arc cairo_stroke(mwcr); } if (crflag) { cairo_destroy(mwcr); mwcr = 0; } 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 cr, int Ferase) // 15.09 { int px3, py3, ww3, hh3; static int ppx3, ppy3, pww3 = 0, phh3; int crflag = 0; int px, py, qx, qy, nc, rs, pok; uint8 *mpix, *mpix1; double R, G, B; double t, dt, t1, t2; zthreadcrash(); if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->mpxb) return; // no image if (pww3 > 0) { // erase prior Fpaint4(ppx3,ppy3,pww3,phh3); // refresh from Mpxb pww3 = 0; } if (Ferase) return; // erase only, done rs = gdk_pixbuf_get_rowstride(Cstate->mpxb->pixbuf); // image row stride nc = gdk_pixbuf_get_n_channels(Cstate->mpxb->pixbuf); // image no. channels mpix = gdk_pixbuf_get_pixels(Cstate->mpxb->pixbuf); // image pixels px3 = cx - cr - 2; // convert pointer center + radius py3 = cy - cr - 2; // to block position, width, length ww3 = 2 * cr + 4; hh3 = 2 * cr + 4; ppx3 = px3; // remember pixel block area ppy3 = py3; // to erase in next call pww3 = ww3; phh3 = hh3; if (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already crflag = 1; } cx = cx * Mscale - Morgx + Dorgx; // convert to window coordinates cy = cy * Mscale - Morgy + Dorgy; cr = cr * Mscale; R = LINE_COLOR[0] / 255.0; // use LINE_COLOR G = LINE_COLOR[1] / 255.0; B = LINE_COLOR[2] / 255.0; cairo_set_source_rgb(mwcr,R,G,B); t1 = t2 = -1; // angle limits of arc to draw dt = 1.0 / cr; for (t = 0; t < 2*PI; t += dt) // loop 0-360 degrees { px = cx + cr * cos(t); // pixel on mouse circle py = cy + cr * 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 && nc > 3) { qx = px + Morgx - Dorgx; // check alpha channel qy = py + Morgy - Dorgy; mpix1 = mpix + qy * rs + qx * nc; if (mpix1[3] < 128) pok = 0; // pixel is transparent } 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(mwcr,cx,cy,cr,t1,t2); // draw accumulated arc cairo_stroke(mwcr); t1 = t2 = -1; // start over } } if (t1 >= 0) { cairo_arc(mwcr,cx,cy,cr,t1,t2); // draw rest of arc cairo_stroke(mwcr); } if (crflag) { cairo_destroy(mwcr); mwcr = 0; } return; } /**************************************************************************/ // Draw ellipse around the mouse pointer. // Prior ellipse will be erased first. // cx, cy, cww, chh: center and axes of ellipse // if Ferase, then erase previous ellipse only. void draw_mousearc(int cx, int cy, int cww, int chh, int Ferase) { int px3, py3, ww3, hh3; static int ppx3, ppy3, pww3 = 0, phh3; int px, py; int crflag = 0; float a, b, a2, b2; float x, y, x2, y2; zthreadcrash(); if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image gdk_window_freeze_updates(gdkwin); // visually smoother if (pww3 > 0) { // erase prior Fpaint4(ppx3,ppy3,pww3,phh3); // refresh from Mpxb pww3 = 0; } if (Ferase) { gdk_window_thaw_updates(gdkwin); // erase only was wanted 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; if (! mwcr) { mwcr = gdk_cairo_create(gdkwin); // create cairo context if not already crflag = 1; } 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); // draw 2 points on ellipse px = cx + x + 0.5; draw_pixel(px,py); } 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); // draw 2 points on ellipse py = cy + y + 0.5; draw_pixel(px,py); } if (crflag) { cairo_destroy(mwcr); mwcr = 0; } gdk_window_thaw_updates(gdkwin); 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 15.10 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)) { zthreadcrash(); 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,"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 15.10 zthreadcrash(); 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 15.10 } 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 15.10 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++) { if (kk < 0) printz("meaningless reference %d \n",kk); // stop gcc optimization bug //// 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 15.10 } 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 15.10 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 15.10 } } if (evtype == GDK_MOTION_NOTIFY) Fdrag = 1; // remember drag is underway if (apset) // 15.10 { 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) // 15.07 { int ap, kk; float minx = 0.01 * splcurve_minx; // % to absolute distance 15.10 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; } // 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; zthreadcrash(); 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 15.10 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) // 15.10 { 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]; zthreadcrash(); 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 15.10 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; int Fpreview; if (! curr_file) return 0; // no image file if (FGW != 'F') return 0; if (checkpend("busy block")) return 0; // blocking function 15.11 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 15.10 zmessageACK(Mwin,ZTX("this function cannot be scripted")); return 0; } free_geomap(); // 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; } if (! resource_lock(Fpaintlock)) return 0; // lock window update 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); // hide select area if present } else E1pxm = PXM_copy(E0pxm); // else use full size image 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 resource_unlock(Fpaintlock); // moved down Fpaintnow(); // update image synchronous return 1; } /**************************************************************************/ // print error message if CEF invalid int CEF_invalid() { if (CEF) return 0; printz("CEF invalid \n"); // 15.05 return 1; } /**************************************************************************/ // process edit cancel // keep: retain zdialog, mousefunc, curves void edit_cancel(int keep) // 15.04 { if (CEF_invalid()) return; wrapup_thread(9); // tell thread to quit, wait 15.05 if (CEF_invalid()) return; if (! resource_lock(Fpaintlock)) return; PXM_free(E1pxm); // free E1, E3, ER PXM_free(E3pxm); PXM_free(ERpxm); PXM_free(E9pxm); if (! keep) { if (CEF->zd == zd_thread) zd_thread = 0; // thread -> zdialog event if (CEF->zd) zdialog_free(CEF->zd); // kill dialog if (CEF->mousefunc == mouseCBfunc) freeMouse(); // if my mouse, free mouse if (CEF->curves) zfree(CEF->curves); // free curves data } CEF = 0; // no current edit func resource_unlock(Fpaintlock); Fpaintnow(); // update image synchronous return; } /**************************************************************************/ // process edit done // keep: retain zdialog, mousefunc, curves void edit_done(int keep) // 15.04 { if (CEF_invalid()) return; wrapup_thread(8); // tell thread to finish, wait 15.05 if (CEF_invalid()) return; if (! resource_lock(Fpaintlock)) return; 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(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 15.10 } else { // not modified PXM_free(E1pxm); // free E1, E3, ER PXM_free(E3pxm); PXM_free(ERpxm); PXM_free(E9pxm); } if (! keep) { if (CEF->zd == zd_thread) zd_thread = 0; // thread -> zdialog event if (CEF->zd) zdialog_free(CEF->zd); // kill dialog if (CEF->mousefunc == mouseCBfunc) freeMouse(); // if my mouse, free mouse if (CEF->curves) zfree(CEF->curves); // free curves data } CEF = 0; // no current edit func resource_unlock(Fpaintlock); 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() // 15.04 { if (CEF_invalid()) return; wait_thread_idle(); // wait for thread idle if (CEF_invalid()) return; if (! CEF->Fpreview) return; // FprevReq ignored if small image if (! resource_lock(Fpaintlock)) return; // 15.02 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; resource_unlock(Fpaintlock); 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 if (! resource_lock(Fpaintlock)) return; // 15.02 PXM_free(ERpxm); // E3 >> redo copy ERpxm = E3pxm; E3pxm = PXM_copy(E1pxm); // E1 >> E3 CEF->Fmods = 0; // reset image modified status resource_unlock(Fpaintlock); 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 if (! resource_lock(Fpaintlock)) return; // 15.02 PXM_free(E3pxm); // redo copy >> E3 E3pxm = ERpxm; ERpxm = 0; CEF->Fmods = 1; // image modified resource_unlock(Fpaintlock); 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 if (! resource_lock(Fpaintlock)) return; // 15.02 PXM_free(ERpxm); // delete redo copy PXM_free(E3pxm); E3pxm = PXM_copy(E1pxm); // E1 >> E3 CEF->Fmods = 0; // reset image modified status resource_unlock(Fpaintlock); 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) // 15.10 { 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_zuserdir(),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) // 15.10 { 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 edit text togbutt check combo comboE " // widget types to save "radio spin hscale vscale colorbutt"; zd = EF->zd; sd = EF->curves; if (! fid) // fid from script { snprintf(dirname,200,"%s/%s",get_zuserdir(),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) // 15.10 { using namespace zfuncs; char filename[200]; FILE *fid; int err; snprintf(filename,200,"%s/%s/%s",get_zuserdir(),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) // 15.10 { using namespace zfuncs; char filename[200]; FILE *fid; int err; snprintf(filename,200,"%s/%s/%s",get_zuserdir(),EF->funcname,"last-used"); fid = fopen(filename,"w"); if (! fid) { zmessageACK(Mwin,"%s \n %s",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, undo or redo all edits. // 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(); else m_undo(0,0); } if (button == 2) // go back to previous edit step 15.07 { 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 image 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(); else m_redo(0,0); } return; } // popup menu response function void undo_redo_choice(GtkWidget *, cchar *menu) // 15.07 { 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() { char *pp, buff[24]; int fid, cc, cc2; int ww, hh, nc; 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 = open(URS_filename,O_WRONLY|O_CREAT|O_TRUNC,0640); // create or overwrite if (! fid) goto writefail; snprintf(buff,24,"fotoxx %05d %05d %d",ww,hh,nc); // write header cc = write(fid,buff,20); if (cc != 20) goto writefail; cc = ww * hh * nc * sizeof(float); // write ww * hh RGB(A) pixels 15.08 cc2 = write(fid,E0pxm->pixels,cc); if (cc2 != cc) goto writefail; close(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); shell_quiet("rm -R %s",tempdir); exit(errno); } // Load E0 from undo/redo file stack // stack position = URS_pos void load_undo() { char *pp, buff[24]; int fid, nn, ww, hh, nc, cc, cc2; if (! resource_lock(Fpaintlock)) return; // 15.02 pp = strstr(URS_filename,"undo_"); if (! pp) zappcrash("undo/redo stack corrupted 1"); snprintf(pp+5,3,"%02d",URS_pos); fid = open(URS_filename,O_RDONLY); if (! fid) zappcrash("undo/redo stack corrupted 2"); cc = read(fid,buff,20); if (cc != 20) zappcrash("undo/redo stack corrupted 3"); nn = sscanf(buff,"fotoxx %d %d %d",&ww,&hh,&nc); if (nn != 3) zappcrash("undo/redo stack corrupted 4"); PXM_free(E0pxm); E0pxm = PXM_make(ww,hh,nc); // 15.08 cc = ww * hh * nc * sizeof(float); cc2 = read(fid,E0pxm->pixels,cc); if (cc2 != cc) zappcrash("undo/redo stack corrupted 5"); close(fid); resource_unlock(Fpaintlock); sa_validate(); // delete area if invalid if (Cdrawin) Fpaintnow(); // update image synchronous 15.09 return; } /**************************************************************************/ // 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 // 15.04 // block edit function mutex flag // 15.04 // 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) zmessageACK(Mwin,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 || Fgtagmod) pend = 1; // metadata edits unsaved 15.03 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 = Fgtagmod = 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 { zthreadcrash(); 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 { zthreadcrash(); if (! Cdrawin) return; if (! gdkwin) return; if (! Mcapture) return; mouseCBfunc = 0; Mcapture = 0; draw_mousearc(0,0,0,0,1); // remove mouse circle 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(); // 15.04 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(); // 15.04 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.003); if (block) continue; zmainloop(); } if (CEF_invalid()) return; } else { while (wthreads_busy) { zsleep(0.003); if (block) continue; zmainloop(); } } return; } /************************************************************************** other support functions ***************************************************************************/ // table for loading and saving adjustable parameters between sessions typedef struct { char name[20]; char type[12]; int count; void *location; } param; #define Nparms 36 param paramTab[Nparms] = { // name type count location { "fotoxx release", "char", 1, &Prelease }, { "first time", "int", 1, &Ffirsttime }, { "window geometry", "int", 4, &mwgeom }, { "thumbnail size", "int", 1, &navi::thumbsize }, { "menu style", "char", 1, &menu_style }, { "dialog font", "char", 1, &dialog_font }, { "icon size", "int", 1, &iconsize }, { "drag option", "int", 1, &Fdragopt }, { "zoom count", "int", 1, &zoomcount }, { "show hidden", "int", 1, &Fshowhidden }, { "last version", "int", 1, &Flastversion }, { "curve node dist %", "float", 1, &splcurve_minx }, { "startup display", "char", 1, &startdisplay }, { "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 }, { "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 command", "char", 1, &DCrawcommand }, { "RAW file types", "char", 1, &RAWfiletypes }, { "trim size", "int", 2, &trimsize }, { "trim buttons", "char", 6, &trimbuttons }, { "trim ratios", "char", 6, &trimratios }, { "edit resize", "int", 2, &editresize }, { "jpeg def quality", "int", 1, &jpeg_def_quality }, { "thumb file size", "int", 1, &thumbfilesize }, { "lens mm", "float", 1, &lens_mm }, { "cycledesktop_album", "char", 1, &cycledesktop_album }, { "cycledesktop_index", "int", 1, &cycledesktop_index }, { "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 15.07 last_gallerytype = 0; // 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_zuserdir()); // 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_zuserdir()); // 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) { if (! resource_lock(Fpaintlock)) return; 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 = Fgtagmod = 0; // no unsaved metadata changes 15.03 Fwarnalpha = 0; // no warn if save image with alpha 15.09 sa_unselect(); // unselect select area draw_toplines(2); // no toplines erase_topcircles(); // no topcircles Ntoptext = 0; // no toptext Fbusy_goal = 0; 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,Frelease); // sparse title 15.03 PXB_free(Fpxb); PXM_free(E0pxm); PXM_free(E1pxm); PXM_free(E3pxm); PXM_free(ERpxm); PXM_free(E8pxm); PXM_free(E9pxm); resource_unlock(Fpaintlock); 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-15.11.1/f.meta.cc0000664000175000017500000104646712616075370013363 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** Fotoxx image editor - image metadata functions. View and edit metadata: EXIF, IPTC, etc. m_meta_view_short metadata short report m_meta_view_long report all metadata meta_view callable function m_captions write captions and comments at top of current image m_edit_metadata primary edit metadata dialog pdate_metadate convert yyyy-mm-dd to yyyymmdd ptime_metatime convert hh:mm:ss to hhmmss metadate_pdate convert yyyymmddhhmmss to yyyy-mm-dd and hh:mm:ss manage_tags maintain list of defined tags add_tag add tag to a tag list 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 m_meta_edit_any dialog to fetch and save any image file metadata by name m_meta_delete dialog to delete any image file metadata by name m_batch_tags batch add and delete tags for selected image files m_batch_renametags convert tag names for all image files using a from-to list m_batch_metadata add/change or delete metadata for selected image files 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 Geotag editing, image mapping, image search functions validate_latlong validate latitude/longitude data init_geolocs load geolocations table from image index file get_geolocs get latitude/longitude for a city or location put_geotags save geotags in image file EXIF and image index file m_load_geomap load a geographic map chosen by user geomap_coordinates convert map pixel position to latitude/longitude geomap_position convert latitude/longitude to map pixel position geomap_paint_dots paint red dots on map where images are located geomap_mousefunc respond to mouse movement and left clicks on geomap free_geomap free huge memory for geomap image m_mapsearch_range set the map search range from a clicked position, kilometers m_edit_geotags edit image city/location, country, latitude, longitude m_batch_geotags add given geotag to selected set of images geotags_choosecity get possible location matches for partial input, choose one web_geocode use web geocoding service to map location to latitude/longitude m_geotag_groups list locations/image counts/time groups, choose one for gallery m_search_images general image search using file names and any metadata EXIF etc. data storage and retrieval 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 one image to another, with revisions exif_server start exiftool server process, send data requests init_image_index initialization for image file index read/write Image index file functions get_sxrec get image index record for image file get_sxrec_min get index data used for gallery view put_sxrec add or update index record for an image file read_sxrec_seq read all index records sequentially, one per call write_sxrec_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" to "hhmm" void metadate_pdate(cchar *metadate, char *pdate, char *ptime); // "yyyymmddhhmm" to "yyyy-mm-dd" and "hh:mm" int get_mouse_tag(GtkTextView *, int px, int py, cchar *); // get tag selected by mouse 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[] >> zdialog widget deftags void defcats_stuff(zdialog *zd); // defined categories >> " widget defcats char *tags_deftags[maxtagcats]; // defined tags: catg: tag1, ... tagN, char tags_recentags[tagRcc] = ""; // recently added tags list /**************************************************************************/ // menu function and popup dialog to show EXIF/IPTC data // window is updated when navigating to another image /**************************************************************************/ // menu function and popup dialog to show EXIF/IPTC data // window is updated when navigating to another image void m_meta_view_short(GtkWidget *, cchar *menu) { if (menu) F1_help_topic = "view_metadata"; meta_view(1); return; } void m_meta_view_long(GtkWidget *, cchar *menu) { if (menu) F1_help_topic = "view_metadata"; meta_view(2); return; } void meta_view(int arg) { 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_latitude_key, exif_longitude_key, iptc_keywords_key, iptc_rating_key, iptc_caption_key, exif_comment_key, exif_usercomment_key, exif_editlog_key }; char *keyval[NK]; static int reportype = 1; char *buff, chsec[12], **text; static char *file = 0, *filen, *chsize; float fsize, fsec; int err, ii, nn, contx = 0; cchar *textdelims = "!-,.:;?/)}]"; cchar *editdelims = ":|,"; GtkWidget *widget; 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; if (arg > 0) reportype = arg; // change report type 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"); if (reportype == 1) zdialog_resize(zdexifview,650,300); else zdialog_resize(zdexifview,800,800); zdialog_run(zdexifview,meta_view_dialog_event); } 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 wclear(widget); if (reportype == 1) // short report better format 15.02 { 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]) > 16) keyval[2][16] = 0; // truncate dates to yyyy-mm-dd hh:mm if (keyval[3] && strlen(keyval[3]) > 16) keyval[3][16] = 0; wprintf(widget,"File %s \n",filen); wprintf(widget,"Size %s %s \n",keyval[0],chsize); wprintf(widget,"Dates photo: %s file: %s \n",keyval[2],keyval[3]); if (keyval[4] || keyval[5]) wprintf(widget,"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); } wprintf(widget,"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 wprintf(widget,"Location %s %s %s %s \n",keyval[10],keyval[11],keyval[12],keyval[13]); if (keyval[14]) wprintf(widget,"Keywords %s \n",keyval[14]); // tags if (keyval[15]) // rating wprintf(widget,"Rating %s \n",keyval[15]); if (keyval[16]) { // caption-abstract nn = breakup_text(keyval[16],text,textdelims,40,60); wprintf(widget,"Caption %s \n",text[0]); for (ii = 1; ii < nn; ii++) wprintf(widget," %s \n",text[ii]); for (ii = 0; ii < nn; ii++) zfree(text[ii]); zfree(text); } if (keyval[17]) { // comment 15.10 nn = breakup_text(keyval[17],text,textdelims,40,60); wprintf(widget,"Comment %s \n",text[0]); for (ii = 1; ii < nn; ii++) wprintf(widget," %s \n",text[ii]); for (ii = 0; ii < nn; ii++) zfree(text[ii]); zfree(text); } if (keyval[18]) { // usercomment 15.10 nn = breakup_text(keyval[18],text,textdelims,40,60); wprintf(widget,"UserComment %s \n",text[0]); for (ii = 1; ii < nn; ii++) wprintf(widget," %s \n",text[ii]); for (ii = 0; ii < nn; ii++) zfree(text[ii]); zfree(text); } if (keyval[19]) { // edit log nn = breakup_text(keyval[19],text,editdelims,40,60); wprintf(widget,"Edits %s \n",text[0]); for (ii = 1; ii < nn; ii++) wprintf(widget," %s \n",text[ii]); for (ii = 0; ii < nn; ii++) zfree(text[ii]); zfree(text); } wscroll(widget,1); for (ii = 0; ii < NK; ii++) if (keyval[ii]) zfree(keyval[ii]); } if (reportype == 2) // long report { snprintf(command,CCC,"exiftool -s -e \"%s\" ", file); while ((buff = command_output(contx,command))) { // run command, output into window wprintf(widget,"%s\n",buff); zfree(buff); } wscroll(widget,1); command_status(contx); } gtk_window_present(MWIN); // keep focus on main window 15.03 return; } // dialog event and completion callback function int meta_view_dialog_event(zdialog *zd, cchar *event) // kill dialog { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (! zd->zstat) return 0; zdialog_free(zdexifview); return 0; } /**************************************************************************/ // 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 *keyvals[2]; char 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; *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]); } *text = 0; if (*caption) strcpy(text,caption); if (*caption && *comment) strcat(text,"\n"); if (*comment) strcat(text,comment); for (int ii = 0; text[ii]; ii++) // replace "\n" with newline chars. 15.01.1 if (text[ii] == '\\' && text[ii+1] == 'n') memmove(text+ii,"\n ",2); if (*text) add_toptext(1,0,0,text,"Sans 10"); return; } /**************************************************************************/ // edit metadata menu function void m_edit_metadata(GtkWidget *, cchar *menu) // overhauled { void edit_imagetags_clickfunc(GtkWidget *widget, int line, int pos); void edit_recentags_clickfunc(GtkWidget *widget, int line, int pos); void edit_matchtags_clickfunc(GtkWidget *widget, int line, int pos); void edit_deftags_clickfunc(GtkWidget *widget, int line, int pos); int editmeta_dialog_event(zdialog *zd, cchar *event); GtkWidget *widget; zdialog *zd; char *ppv, pdate[12], ptime[12]; char cctext[exif_maxcc+50]; char RN[4] = "R0"; int ii; if (menu) F1_help_topic = "edit_metadata"; if (! curr_file) { if (zdeditmeta) zdialog_free(zdeditmeta); zdeditmeta = 0; return; } /** ________________________________________________________ | Edit Metadata | | | | Image File: filename.jpg | | Image Date: [_________] Time: [______] [prev] | | Rating (stars): ʘ 1 ʘ 2 ʘ 3 ʘ 4 ʘ 5 ʘ 6 | | Caption [___________________________________________] | | Comments [__________________________________________] | | | | Image Tags [________________________________________] | | Recent Tags [_______________________________________] | | Type New Tag [______________________________________] | | Matching Tags [_____________________________________] | | | | Defined Tags Category [________________________|v] | | ___________________________________________________ | | | | | | | | | | | | | | | | | | | | | | | | | | |___________________________________________________| | | | | [Manage Tags] [Apply] [Cancel] | |________________________________________________________| **/ if (! zdeditmeta) // (re) start edit dialog { zd = zdialog_new(ZTX("Edit Metadata"),Mwin,Bmanagetags,Bapply,Bcancel,null); zdeditmeta = zd; zdialog_add_ttip(zd,Bapply,ZTX("save metadata to file")); // 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 yyyy-mm-dd [__________] Time hh:mm [_____] [prev] zdialog_add_widget(zd,"hbox","hbdt","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labdate","hbdt",ZTX("Image Date"),"space=3"); zdialog_add_widget(zd,"entry","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,"entry","time","hbdt",0,"size=8"); zdialog_add_widget(zd,"button","ppdate","hbdt",Bprev,"space=8"); // 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,"frame","frame4","hbcap",0,"space=3|expand"); zdialog_add_widget(zd,"edit","caption","frame4",0,"wrap"); // 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,"frame","frcom","hbcom",0,"space=3|expand"); zdialog_add_widget(zd,"edit","comments","frcom",0,"wrap"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=3"); // Enter New Tag [_____________________________________] 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,"frame","frnt","hbnt",0,"space=3|expand"); // works OK, but GTK warnings galore //zdialog_add_widget(zd,"edit","newtag","frnt"); zdialog_add_widget(zd,"entry","newtag","hbnt"); // 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"); // 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,"frame","frit","hbit",0,"space=3|expand"); zdialog_add_widget(zd,"text","imagetags","frit",0,"wrap"); // 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"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=3"); // Defined Tags Category 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"); load_deftags(); // stuff defined tags into dialog deftags_stuff(zd,"ALL"); defcats_stuff(zd); // and defined categories 15.08 widget = zdialog_widget(zd,"imagetags"); // tag widget mouse functions textwidget_set_clickfunc(widget,edit_imagetags_clickfunc); widget = zdialog_widget(zd,"recentags"); textwidget_set_clickfunc(widget,edit_recentags_clickfunc); widget = zdialog_widget(zd,"matchtags"); textwidget_set_clickfunc(widget,edit_matchtags_clickfunc); widget = zdialog_widget(zd,"deftags"); textwidget_set_clickfunc(widget,edit_deftags_clickfunc); zdialog_resize(zd,400,500); // run dialog zdialog_run(zd,editmeta_dialog_event); } zd = zdeditmeta; ppv = (char *) strrchr(curr_file,'/'); zdialog_stuff(zd,"file",ppv+1); // stuff dialog fields from curr. image file metadate_pdate(meta_pdate,pdate,ptime); zdialog_stuff(zd,"date",pdate); 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); zdialog_stuff(zd,"imagetags",meta_tags); zdialog_stuff(zd,"recentags",tags_recentags); gtk_window_present(MWIN); // keep focus on main window 15.03 return; } // mouse click functions for various text widgets for tags void edit_imagetags_clickfunc(GtkWidget *widget, int line, int pos) // existing image tag was clicked { char *txline, *txtag, end = 0; txline = textwidget_get_line(widget,line,0); if (! txline) return; txtag = textwidget_get_word(txline,pos,",;",end); if (! txtag) { zfree(txline); return; } del_tag(txtag,meta_tags); // remove tag from image zdialog_stuff(zdeditmeta,"imagetags",meta_tags); Fmetamod++; // note change 15.03 zfree(txline); zfree(txtag); return; } void edit_recentags_clickfunc(GtkWidget *widget, int line, int pos) // recent tag was clicked { char *txline, *txtag, end = 0; txline = textwidget_get_line(widget,line,0); if (! txline) return; txtag = textwidget_get_word(txline,pos,",;",end); if (! txtag) { zfree(txline); return; } add_tag(txtag,meta_tags,tagFcc); // add recent tag to image zdialog_stuff(zdeditmeta,"imagetags",meta_tags); Fmetamod++; // note change 15.03 zfree(txline); zfree(txtag); return; } void edit_matchtags_clickfunc(GtkWidget *widget, int line, int pos) // matching tag was clicked 15.07 { char *txline, *txtag, end = 0; txline = textwidget_get_line(widget,line,0); if (! txline) return; txtag = textwidget_get_word(txline,pos,",;",end); if (! txtag) { zfree(txline); return; } add_tag(txtag,meta_tags,tagFcc); // add matching tag to image Fmetamod++; // note change 15.03 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(txline); zfree(txtag); return; } void edit_deftags_clickfunc(GtkWidget *widget, int line, int pos) // defined tag was clicked { char *txline, *txtag, end = 0; txline = textwidget_get_line(widget,line,0); if (! txline) return; txtag = textwidget_get_word(txline,pos,",;:",end); if (! txtag || end == ':') { zfree(txline); 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 15.03 add_recentag(txtag); // and add to recent tags zdialog_stuff(zdeditmeta,"recentags",tags_recentags); zfree(txline); 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 int ii, jj, nt, cc1, cc2, ff; 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"; if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 if (! curr_file) return 1; if (strstr("date time caption comments",event)) // note change but process later Fmetamod++; if (strmatch(event,"ppdate")) { // repeat last date used if (*meta_ppdate) { metadate_pdate(meta_ppdate,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 (strmatch(event,"defcats")) { // new tag category selection 15.08 zdialog_fetch(zd,"defcats",catgname,tagcc); deftags_stuff(zd,catgname); } if (strmatch(event,"newtag")) // new tag is being typed in 15.07 { 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,"enter")) // KB Enter, new tag finished 15.07 { 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(); zdialog_show(zd,1); return 1; } if (zd->zstat != 2) { // cancel or [x] zdialog_free(zdeditmeta); // kill dialog zdeditmeta = 0; Fmetamod = 0; return 1; } zd->zstat = 0; // apply - keep dialog active gtk_window_present(MWIN); // keep focus on main window if (! Fmetamod) return 1; // nothing changed 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" save_filemeta(curr_file); // save metadata changes to image file return 1; } // 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 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; if (metadate[12] > '0' || metadate[13] > '0') { // append :ss only if not :00 memcpy(ptime+6,metadate+12,2); ptime[5] = ':'; ptime[8] = 0; } } else *pdate = *ptime = 0; // missing return; } // validate a date/time string formatted "yyyymmddhhmmss" (ss optional) // return 0 if bad, 1 if OK // valid year is 0000 to 2099 int datetimeOK(char *datetime) // 15.08 { int monlim[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int year, mon, day, hour, min, sec; char temp[8]; if (strlen(datetime) < 12) return 0; strncpy0(temp,datetime,5); year = atoi(temp); strncpy0(temp,datetime+4,3); mon = atoi(temp); strncpy0(temp,datetime+6,3); day = atoi(temp); strncpy0(temp,datetime+8,3); hour = atoi(temp); strncpy0(temp,datetime+10,3); min = atoi(temp); if (strlen(datetime) == 14) { strncpy0(temp,datetime+12,3); sec = atoi(temp); } 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; } /**************************************************************************/ // manage tags function - auxilliary dialog zdialog *zdmanagetags = 0; void manage_tags() { void manage_deftags_clickfunc(GtkWidget *widget, int line, int pos); int managetags_dialog_event(zdialog *zd, cchar *event); GtkWidget *widget; zdialog *zd; 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,"entry","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,"entry","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_clickfunc(widget,manage_deftags_clickfunc); load_deftags(); // stuff defined tags into dialog deftags_stuff(zd,"ALL"); zdialog_resize(zd,0,400); zdialog_run(zd,managetags_dialog_event); // 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) // tag or tag category was clicked { char *txline, *txword, end = 0; txline = textwidget_get_line(widget,line,0); if (! txline) return; txword = textwidget_get_word(txline,pos,",;:",end); if (! txword) { zfree(txline); return; } if (end == ':') zdialog_stuff(zdmanagetags,"catg",txword); // selected category >> dialog widget else zdialog_stuff(zdmanagetags,"tag",txword); // selected tag >> dialog widget zfree(txline); zfree(txword); return; } // dialog event and completion callback function int managetags_dialog_event(zdialog *zd, cchar *event) { void tag_orphans(); char tag[tagcc], catg[tagcc]; int err, changed = 0; if (strmatch(event,"enter")) zd->zstat = 2; // [done] if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 if (zd->zstat) { if (zd->zstat == 1) { // report orphan tags zd->zstat = 0; // keep dialog active tag_orphans(); } else { // done or [x] 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; } /**************************************************************************/ // 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) // image tags were changed Fmetamod++; return 0; } // 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) // image tags were changed Fmetamod++; 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; sxrec_t sxrec; int ii, jj, ntags, err, cc, tcc, ftf; 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 15.07 pp2++; } tags_deftags[ncats] = zstrdup(pp1); // tags_deftags[ii] ncats++; // = category: tag1, tag2, ... tagN, if (ncats == maxtagcats-1) goto toomanycats; // leave 1 for "nocatg" } err = fclose(fid); fid = 0; // 15.07 if (err) goto deftagsfilerr; } nocat = ncats; // make last category "nocatg" for ncats++; // unmatched tags in image_index recs. tags_deftags[nocat] = (char *) zmalloc(tagGcc); strcpy(tags_deftags[nocat],"nocatg: "); nocatcc = 8; ftf = 1; // read all image index recs. while (true) { err = read_sxrec_seq(sxrec,ftf); if (err) break; pp1 = sxrec.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; } zfree(sxrec.file); zfree(sxrec.tags); zfree(sxrec.comms); zfree(sxrec.capt); zfree(sxrec.gtags); } 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,':'); 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; // 15.07 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; } // sort the categories in ascending order // leave "nocatg" at the end for (ii = 0; ii < ncats-1; ii++) for (jj = ii+1; jj < ncats-1; jj++) { pp1 = tags_deftags[ii]; pp2 = tags_deftags[jj]; if (strcasecmp(pp1,pp2) > 0) { tags_deftags[ii] = pp2; tags_deftags[jj] = pp1; } } return; toomanycats: zmessLogACK(Mwin,"more than %d categories",maxtagcats); if (fid) fclose(fid); return; cattoobig: zmessLogACK(Mwin,"category %s is too big",catg); if (fid) fclose(fid); return; toomanytags: // 15.07 zmessLogACK(Mwin,"category %s has too many tags",catg); if (fid) fclose(fid); return; deftagsfilerr: zmessLogACK(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+1]) break; // omit last category, "nocatg" 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: zmessLogACK(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"); // revised 15.05 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 return 0; oldcatg: // logic simplified 15.05 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,", "); 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) // 15.08 { GtkWidget *widget; int ii, ff, cc; char catgname[tagcc+4]; char *pp1, *pp2; widget = zdialog_widget(zd,"deftags"); wclear(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,": "); wprintx(widget,0,catgname,1); // "category: " in bold text pp2++; if (*pp2 == ' ') pp2++; if (*pp2) wprintx(widget,0,pp2); // "cat1, cat2, ... catN," wprintx(widget,0,"\n"); } return; } // Stuff combo box "defcats" with "ALL" + all defined categories void defcats_stuff(zdialog *zd) // 15.08 { 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() { FILE *fid; sxrec_t sxrec; int ii, cc, err, ftf; int Ndeftags; char **deftags; char usedtag[tagcc], tagsbuff[tagGcc]; char *pp1, *pp2, text[20]; 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); } ftf = 1; // read all image index recs. while (true) { err = read_sxrec_seq(sxrec,ftf); if (err) break; pp1 = sxrec.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 (ii = 0; ii < Ndeftags; ii++) // find in defined tags if (strmatch(usedtag,deftags[ii])) break; if (ii < Ndeftags) { // found zfree(deftags[ii]); Ndeftags--; while (ii < Ndeftags) { // defined tag is in use deftags[ii] = deftags[ii+1]; // remove from list and pack down ii++; } } pp1 = pp2; } } write_popup_text("open","unused tags",200,200,Mwin); for (ii = 0; ii < Ndeftags; ii++) write_popup_text("write",deftags[ii]); snprintf(text,20,"%d unused tags",Ndeftags); write_popup_text("write",text); for (ii = 0; ii < Ndeftags; ii++) zfree(deftags[ii]); zfree(deftags); return; } /**************************************************************************/ // 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); char keyname[40], keydata[exif_maxcc]; cchar *pp1[1]; char *pp2[1]; if (menu) F1_help_topic = "edit_any_metadata"; if (! curr_file) return; if (! zdexifedit) // popup dialog if not already { zdexifedit = zdialog_new(ZTX("Edit Metadata"),Mwin,Bfetch,Bsave,Bdone,null); zdialog_add_widget(zdexifedit,"vbox","hb1","dialog"); zdialog_add_widget(zdexifedit,"hbox","hbkey","dialog",0,"space=2"); zdialog_add_widget(zdexifedit,"hbox","hbdata","dialog",0,"space=2"); zdialog_add_widget(zdexifedit,"label","labkey","hbkey",ZTX("key name")); zdialog_add_widget(zdexifedit,"entry","keyname","hbkey",0,"size=20"); zdialog_add_widget(zdexifedit,"label","labdata","hbdata",ZTX("key value")); zdialog_add_widget(zdexifedit,"entry","keydata","hbdata",0,"expand"); zdialog_run(zdexifedit,meta_edit_any_dialog_event); } zdialog_fetch(zdexifedit,"keyname",keyname,40); // get key name from dialog strCompress(keyname); 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(zdexifedit,"keydata",keydata); // stuff into dialog } return; } // dialog event and completion callback function int meta_edit_any_dialog_event(zdialog *zd, cchar *event) { char keyname[40], keydata[exif_maxcc]; cchar *pp1[1], *pp2[1]; char *pp3[1]; int err; if (strmatch(event,"escape")) zd->zstat = 4; // escape = cancel 15.07 if (! zd->zstat) return 1; if (! curr_file) return 1; zdialog_fetch(zd,"keyname",keyname,40); zdialog_fetch(zd,"keydata",keydata,exif_maxcc); strCompress(keyname); // remove blanks if (zd->zstat == 1) // fetch { zd->zstat = 0; if (! *keyname) return 0; pp1[0] = keyname; exif_get(curr_file,pp1,pp3,1); if (pp3[0]) { strncpy0(keydata,pp3[0],exif_maxcc); zfree(pp3[0]); } else *keydata = 0; zdialog_stuff(zd,"keydata",keydata); } else if (zd->zstat == 2) // save { zd->zstat = 0; // keep dialog active if (! *keyname) return 1; pp1[0] = keyname; pp2[0] = keydata; err = exif_put(curr_file,pp1,pp2,1); // change metadata in image file if (err) zmessageACK(Mwin,"error: %s",strerror(err)); load_filemeta(curr_file); // update image index in case 15.03 update_image_index(curr_file); // searchable metadata item updated if (zdexifview) meta_view(0); // update exif view if active } else zdialog_free(zdexifedit); // other return 1; } /**************************************************************************/ // 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; F1_help_topic = "delete_metadata"; if (! curr_file) return; zd = zdialog_new(ZTX("Delete Metadata"),Mwin,Bapply,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); 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,"entry","keyname","hb1",0,"size=20"); zdialog_stuff(zd,"key1",1); zdialog_run(zd,meta_delete_dialog_event); return; } // dialog event and completion callback function int meta_delete_dialog_event(zdialog *zd, cchar *event) { int kall, key1; char keyname[40]; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (! zd->zstat) return 1; // wait for completion if (zd->zstat != 1 || ! curr_file) { // canceled zdialog_free(zd); return 1; } zd->zstat = 0; // dialog remains active gtk_window_present(MWIN); // keep focus on main window 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 15.03 update_image_index(curr_file); // searchable metadata deleted if (zdexifview) meta_view(0); // update exif view if active return 1; } /**************************************************************************/ // menu function - add and remove tags for many files at once namespace batchtags { char **filelist = 0; // files to process int filecount = 0; // file count char addtags[tagMcc]; // tags to add, list char deltags[tagMcc]; // tags to remove, list } void m_batch_tags(GtkWidget *, cchar *) // combine batch add/del tags { using namespace batchtags; void batch_addtags_clickfunc(GtkWidget *widget, int line, int pos); void batch_deltags_clickfunc(GtkWidget *widget, int line, int pos); void batch_deftags_clickfunc(GtkWidget *widget, int line, int pos); int batch_tags_dialog_event(zdialog *zd, cchar *event); char *ptag, *file; int zstat, ii, jj, err; 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 [_________________________] | | | | Defined Tags Category [________________________|v] | | ___________________________________________________ | | | | | | | | | | | | | | | | | | | | | | | | | | |___________________________________________________| | | | | [Manage Tags] [Proceed] [Cancel] | |________________________________________________________| ***/ zd = zdialog_new(ZTX("Batch Add/Remove Tags"),Mwin,Bmanagetags,Bproceed,Bcancel,null); zdbatchtags = zd; 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","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"); 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_stuff(zd,"radadd",1); zdialog_stuff(zd,"raddel",0); load_deftags(); // stuff defined tags into dialog deftags_stuff(zd,"ALL"); defcats_stuff(zd); // and defined categories 15.08 filelist = 0; filecount = 0; *addtags = *deltags = 0; widget = zdialog_widget(zd,"addtags"); // tag widget mouse functions textwidget_set_clickfunc(widget,batch_addtags_clickfunc); widget = zdialog_widget(zd,"deltags"); textwidget_set_clickfunc(widget,batch_deltags_clickfunc); widget = zdialog_widget(zd,"deftags"); textwidget_set_clickfunc(widget,batch_deftags_clickfunc); zdialog_resize(zd,500,400); // run dialog zdialog_run(zd,batch_tags_dialog_event); zstat = zdialog_wait(zd); // wait for dialog completion zdialog_free(zd); // kill dialog zdbatchtags = 0; if (zstat != 2) // cancel { if (filecount) { for (ii = 0; filelist[ii]; ii++) zfree(filelist[ii]); zfree(filelist); filecount = 0; } Fblock = 0; return; } write_popup_text("open","Batch Tags",500,200,Mwin); // status monitor popup window for (ii = 0; filelist[ii]; ii++) // loop all selected files { file = filelist[ii]; // display image err = f_open(file,0,0,0); if (err) continue; write_popup_text("write",file); // report progress zmainloop(); 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 } write_popup_text("write","COMPLETED"); for (ii = 0; filelist[ii]; ii++) zfree(filelist[ii]); zfree(filelist); Fblock = 0; return; } // mouse click functions for widgets holding tags void batch_addtags_clickfunc(GtkWidget *widget, int line, int pos) // a tag in the add list was clicked { using namespace batchtags; char *txline, *txtag, end; txline = textwidget_get_line(widget,line,0); if (! txline) return; txtag = textwidget_get_word(txline,pos,",;",end); if (! txtag) { zfree(txline); return; } del_tag(txtag,addtags); // remove tag from list zdialog_stuff(zdbatchtags,"addtags",addtags); zfree(txline); zfree(txtag); return; } void batch_deltags_clickfunc(GtkWidget *widget, int line, int pos) // a tag in the remove list was clicked { using namespace batchtags; char *txline, *txtag, end; txline = textwidget_get_line(widget,line,0); if (! txline) return; txtag = textwidget_get_word(txline,pos,",;",end); if (! txtag) { zfree(txline); return; } del_tag(txtag,deltags); // remove tag from list zdialog_stuff(zdbatchtags,"deltags",deltags); zfree(txline); zfree(txtag); return; } void batch_deftags_clickfunc(GtkWidget *widget, int line, int pos) // a defined tag was clicked { using namespace batchtags; char *txline, *txtag, end; int radadd; txline = textwidget_get_line(widget,line,0); if (! txline) return; txtag = textwidget_get_word(txline,pos,",;:",end); if (! txtag || end == ':') { zfree(txline); return; } // tag category clicked, ignore 15.5 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); } zfree(txline); zfree(txtag); return; } // batchTags dialog event function int batch_tags_dialog_event(zdialog *zd, cchar *event) { using namespace batchtags; int ii; char countmess[50], catgname[tagcc]; if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 if (zd->zstat) { 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 (! filecount || (*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 { if (filelist) { // free prior list for (ii = 0; filelist[ii]; ii++) zfree(filelist[ii]); zfree(filelist); } zdialog_show(zd,0); // hide parent dialog filelist = gallery_getfiles(); // get file list from user zdialog_show(zd,1); if (filelist) // count files in list for (ii = 0; filelist[ii]; ii++); else ii = 0; filecount = ii; snprintf(countmess,50,Bfileselected,filecount); zdialog_stuff(zd,"labcount",countmess); } if (strmatch(event,"defcats")) { // new tag category selection 15.08 zdialog_fetch(zd,"defcats",catgname,tagcc); deftags_stuff(zd,catgname); } return 1; } /**************************************************************************/ // Convert tag names for all image files, based on a list // of old tag names and corresponding new tag names. void m_batch_renametags(GtkWidget *, cchar *menu) // 15.07 { char *tagfile, buff[200], logrec[200]; char *taglist[maxtags][2], *filelist[maximages]; char *oldtaglist[100], *newtaglist[100]; char *pp, *file; char *oldtag, *newtag, *filetag; int ntags = 0, nfiles = 0, nlist; int err, ii, jj, kk, ff, yn, ftf = 1; FILE *fid = 0; sxrec_t sxrec; F1_help_topic = "batch_rename_tags"; if (checkpend("all")) return; // check nothing pending Fblock = 1; tagfile = zgetfile(ZTX("tag names file"),MWIN,"file",getenv("HOME")); // get tag names file from user if (! tagfile) { Fblock = 0; return; } fid = fopen(tagfile,"r"); // open file if (! fid) { zmessageACK(Mwin,strerror(errno)); Fblock = 0; return; } write_popup_text("open","batch rename tags",600,400,Mwin); while (true) // read old and new tag names { pp = fgets_trim(buff,200,fid,1); if (! pp) break; if (blank_null(pp)) continue; oldtag = (char *) strField(buff,',',1); if (! oldtag) continue; taglist[ntags][0] = zstrdup(oldtag); // tag to replace newtag = (char *) strField(buff,',',2); if (newtag) taglist[ntags][1] = zstrdup(newtag); // replacement tag else taglist[ntags][1] = 0; // none - tag will be deleted snprintf(logrec,200,"old tag: %s new tag: %s",oldtag,newtag); write_popup_text("write",logrec); ntags++; if (ntags == maxtags) { zmessageACK(Mwin,"max tags exceeded: %d",maxtags); goto cleanup; } } fclose(fid); fid = 0; while (true) // read image index file { err = read_sxrec_seq(sxrec,ftf); // loop each image file if (err) break; ff = 0; if (sxrec.tags) { // search for tags to rename for (ii = 1; ; ii++) { pp = (char *) strField(sxrec.tags,',',ii); if (! pp) break; if (strmatch(pp,"null")) continue; for (jj = 0; jj < ntags; jj++) { if (strmatchcase(pp,taglist[jj][0])) { // this file has one or more tags ff = 1; // that will be renamed break; } } if (ff) break; } } if (ff) { filelist[nfiles] = zstrdup(sxrec.file); // add to list of files to process nfiles++; snprintf(logrec,200,"file included: %s",sxrec.file); write_popup_text("write",logrec); } zfree(sxrec.file); // free index rec. memory if (sxrec.tags) zfree(sxrec.tags); if (sxrec.capt) zfree(sxrec.capt); if (sxrec.comms) zfree(sxrec.comms); if (sxrec.gtags) zfree(sxrec.gtags); } 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 { file = filelist[ii]; // open image file err = f_open(file,0,0,0); if (err) continue; write_popup_text("write",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 oldtag = taglist[kk][0]; newtag = taglist[kk][1]; if (strmatchcase(filetag,oldtag)) { // file tag matches tag to replace oldtaglist[nlist] = oldtag; // save old and new tags newtaglist[nlist] = newtag; nlist++; break; // next file tag } } } for (jj = 0; jj < nlist; jj++) // loop tags to remove err = del_tag(oldtaglist[jj],meta_tags); for (jj = 0; jj < nlist; jj++) { // loop tags to add if (! newtaglist[jj]) continue; // must be after removals write_popup_text("write",newtaglist[jj]); err = add_tag(newtaglist[jj],meta_tags,tagFcc); if (err && err != 1) write_popup_text("write","ERROR"); // ignore if already there } save_filemeta(file); // save tag changes } write_popup_text("write","COMPLETED"); cleanup: // free resources Fblock = 0; if (fid) fclose(fid); for (ii = 0; ii < ntags; ii++) { zfree(taglist[ii][0]); if (taglist[ii][1]) zfree(taglist[ii][1]); } for (ii = 0; ii < nfiles; ii++) zfree(filelist[ii]); return; } /**************************************************************************/ // batch add or change any EXIF/IPTC metadata namespace batchmeta { char **filelist = 0; // files to process int filecount = 0; // file count zdialog *zd; } // menu function void m_batch_metadata(GtkWidget *, cchar *menu) { using namespace batchmeta; int batch_metadata_dialog_event(zdialog *zd, cchar *event); int ii, jj, err, zstat, nkeys; char keynameN[12] = "keynameN", keyvalueN[12] = "keyvalueN"; char keyname[40], keyvalue[exif_maxcc]; cchar *pp1[5], *pp2[5]; char *file, text[200]; F1_help_topic = "batch_metadata"; if (checkpend("all")) return; // check nothing pending Fblock = 1; /** ____________________________________________ | Batch Metadata | | | | [Select Files] NN files selected | | [Short List] [Full List] | | | | key name key value | | [______________] [_____________________] | | [______________] [_____________________] | | [______________] [_____________________] | | [______________] [_____________________] | | [______________] [_____________________] | | | | [apply] [cancel] | |____________________________________________| **/ zd = zdialog_new(ZTX("Batch Metadata"),Mwin,Bapply,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","hblists","dialog",0,"space=3"); zdialog_add_widget(zd,"button","shortlist","hblists",ZTX("Short List"),"space=5"); zdialog_add_widget(zd,"button","fulllist","hblists",ZTX("Full List"),"space=5"); zdialog_add_widget(zd,"hbox","hbkeys","dialog",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,"entry","keyname0","vbname",0,"size=20"); zdialog_add_widget(zd,"entry","keyname1","vbname",0,"size=20"); zdialog_add_widget(zd,"entry","keyname2","vbname",0,"size=20"); zdialog_add_widget(zd,"entry","keyname3","vbname",0,"size=20"); zdialog_add_widget(zd,"entry","keyname4","vbname",0,"size=20"); zdialog_add_widget(zd,"entry","keyvalue0","vbvalue",0,"expand"); zdialog_add_widget(zd,"entry","keyvalue1","vbvalue",0,"expand"); zdialog_add_widget(zd,"entry","keyvalue2","vbvalue",0,"expand"); zdialog_add_widget(zd,"entry","keyvalue3","vbvalue",0,"expand"); zdialog_add_widget(zd,"entry","keyvalue4","vbvalue",0,"expand"); filelist = 0; nkeys = 0; retry: zstat = zdialog_run(zd,batch_metadata_dialog_event); // run dialog zstat = zdialog_wait(zd); // wait for completion if (zstat != 1) goto cleanup; // cancel for (ii = jj = 0; ii < 5; 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")); zfuncs::zdialog_busy--; goto retry; } if (filelist == 0) { zmessageACK(Mwin,ZTX("no files selected")); zfuncs::zdialog_busy--; goto retry; } write_popup_text("open","Batch Metadata",500,200,Mwin); // status monitor popup window for (ii = 0; ii < nkeys; ii++) { if (*pp2[ii]) snprintf(text,200,"%s = %s",pp1[ii],pp2[ii]); else snprintf(text,200,"%s = DELETED",pp1[ii]); write_popup_text("write",text); } ii = zdialog_choose(Mwin,Bproceed,Bproceed,Bcancel,null); if (ii != 1) { zfuncs::zdialog_busy--; goto retry; } zdialog_free(zd); zd = 0; for (ii = 0; filelist[ii]; ii++) // loop all selected files { file = filelist[ii]; // display image err = f_open(file,0,0,0); if (err) continue; write_popup_text("write",file); // report progress zmainloop(); 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 } write_popup_text("write","COMPLETED"); 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; if (filelist) { for (ii = 0; filelist[ii]; ii++) // free memory zfree(filelist[ii]); zfree(filelist); } filelist = 0; Fblock = 0; return; } // dialog event and completion callback function int batch_metadata_dialog_event(zdialog *zd, cchar *event) { using namespace batchmeta; void batch_metadata_click(GtkWidget *,int line,int pos); int ii; char countmess[50], filename[200]; char buff[100], *pp; GtkWidget *textwin; FILE *fid; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel if (strmatch(event,"files")) // select images to process { if (filelist) { // free prior list for (ii = 0; filelist[ii]; ii++) zfree(filelist[ii]); zfree(filelist); filelist = 0; } zdialog_show(zd,0); // hide parent dialog filelist = gallery_getfiles(); // get file list from user zdialog_show(zd,1); if (filelist) // count files in list for (ii = 0; filelist[ii]; ii++); else ii = 0; snprintf(countmess,50,Bfileselected,ii); zdialog_stuff(zd,"labcount",countmess); } if (strmatch(event,"shortlist")) // 15.10 { snprintf(filename,200,"%s/metadata_short_list",get_zuserdir()); fid = fopen(filename,"r"); if (! fid) { zmessageACK(Mwin,"%s \n %s",filename,strerror(errno)); return 1; } textwin = write_popup_text("open","Tag Short List",300,400,Mwin); while (true) { pp = fgets_trim(buff,100,fid,1); if (! pp) break; write_popup_text("write",buff); } fclose(fid); write_popup_text("top"); textwidget_set_clickfunc(textwin,batch_metadata_click); } if (strmatch(event,"fulllist")) zmessageACK(Mwin,ZTX("The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names")); return 1; } // get clicked tag name from short list and insert into dialog void batch_metadata_click(GtkWidget *widget, int line, int pos) // 15.10 { using namespace batchmeta; int ii; char *pp; char keynameX[12] = "keynameX"; char keyname[60]; pp = textwidget_get_line(widget,line,1); // get clicked line, highlight for (ii = 0; ii < 5; ii++) { // find 1st empty dialog key name keynameX[7] = '0' + ii; zdialog_fetch(zd,keynameX,keyname,60); if (*keyname <= ' ') break; } if (ii < 5) zdialog_stuff(zd,keynameX,pp); return; } /**************************************************************************/ // image file EXIF/IPTC data >> memory data: // meta_pdate, meta_rating, meta_tags, meta_comments, meta_caption, // meta_city, meta_country, meta_latitude, meta_longitude void load_filemeta(cchar *file) { int ii, jj, cc; char *pp; cchar *exifkeys[10] = { 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_latitude_key, exif_longitude_key }; char *ppv[10], *imagedate, *imagekeywords, *imagestars, *imagesize; char *imagecomms, *imagecapt; char *imagecity, *imagecountry, *imagelatitude, *imagelongitude; *meta_tags = *meta_pdate = *meta_comments = *meta_caption = 0; strcpy(meta_rating,"0"); *meta_city = *meta_country = *meta_latitude = *meta_longitude = 0; exif_get(file,exifkeys,ppv,10); // get metadata from image file imagedate = ppv[0]; imagekeywords = ppv[1]; imagestars = ppv[2]; imagesize = ppv[3]; imagecomms = ppv[4]; imagecapt = ppv[5]; imagecity = ppv[6]; imagecountry = ppv[7]; imagelatitude = ppv[8]; imagelongitude = ppv[9]; if (imagedate) { exif_tagdate(imagedate,meta_pdate); // EXIF date/time >> yyyymmddhhmmss strcpy(meta_ppdate,meta_pdate); 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 (imagecity) { // geotags strncpy0(meta_city,imagecity,99); zfree(imagecity); } else strcpy(meta_city,"null"); // replace missing data with "null" if (imagecountry) { strncpy0(meta_country,imagecountry,99); zfree(imagecountry); } else strcpy(meta_country,"null"); if (imagelatitude) { strncpy0(meta_latitude,imagelatitude,12); zfree(imagelatitude); } else strcpy(meta_latitude,"null"); if (imagelongitude) { strncpy0(meta_longitude,imagelongitude,12); zfree(imagelongitude); } else strcpy(meta_longitude,"null"); Fmetamod = Fgtagmod = 0; // no pending changes 15.03 return; } // add metadata in memory to image file EXIF/IPTC data and image_index recs. void save_filemeta(cchar *file) { cchar *exifkeys[10] = { 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_latitude_key, exif_longitude_key }; cchar *exifdata[10]; char imagedate[24]; *imagedate = 0; if (*meta_pdate) { tag_exifdate(meta_pdate,imagedate); // yyyymmddhhmmss >> EXIF date/time strcpy(meta_ppdate,meta_pdate); } 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 (strmatch(meta_city,"null")) exifdata[6] = ""; // geotags else exifdata[6] = meta_city; // if "null" erase EXIF if (strmatch(meta_country,"null")) exifdata[7] = ""; else exifdata[7] = meta_country; if (strmatch(meta_latitude,"null") || strmatch(meta_longitude,"null")) exifdata[8] = exifdata[9] = ""; else { exifdata[8] = meta_latitude; exifdata[9] = meta_longitude; } exif_put(file,exifkeys,exifdata,10); // write EXIF update_image_index(file); // update image index file if (zdexifview) meta_view(0); // live EXIF/IPTC update Fmetamod = Fgtagmod = 0; // no pending changes 15.03 return; } // update image index record (replace updated file data) void update_image_index(cchar *file) // overhauled { char gtags[200]; int err; sxrec_t sxrec; STATB statb; err = stat(file,&statb); // build new metadata record to insert if (err) { // or replace zmessageACK(Mwin,ZTX("file not found")); return; } memset(&sxrec,0,sizeof(sxrec_t)); sxrec.file = (char *) file; // image filespec compact_time(statb.st_mtime,sxrec.fdate); // convert file date to "yyyymmddhhmmss" strncpy0(sxrec.pdate,meta_pdate,15); // photo date, "yyyymmddhhmmss" sxrec.rating[0] = meta_rating[0]; // rating '0' to '5' stars sxrec.rating[1] = 0; strncpy0(sxrec.size,meta_size,15); // 2345x12345 if (*meta_tags) // tags sxrec.tags = meta_tags; if (*meta_caption) // user caption sxrec.capt = meta_caption; if (*meta_comments) // user comments sxrec.comms = meta_comments; if (! *meta_city) strcpy(meta_city,"null"); // geotags if (! *meta_country) strcpy(meta_country,"null"); if (! *meta_latitude) strcpy(meta_latitude,"null"); // "null" for city/country is searchable if (! *meta_longitude) strcpy(meta_longitude,"null"); snprintf(gtags,200,"%s^ %s^ %s^ %s",meta_city, meta_country, meta_latitude, meta_longitude); sxrec.gtags = gtags; put_sxrec(&sxrec,file); return; } // delete given image file from image index recs. void delete_image_index(cchar *file) { put_sxrec(null,file); return; } /************************************************************************** GEOTAG functions ***************************************************************************/ struct geolocs_t { // geotag location table, memory DB char *city, *country; char *lati, *longi; float flati, flongi; }; geolocs_t *geolocs; // maps location names <-> latitude/longitude int Ngeolocs = 0; // size of geolocations table zdialog *zd_geotags; // active zdialog to get geotags data float geomap_range = 10; // default geotag search range, km char prevcity[100] = "", prevcountry[100] = ""; // last geotag data used char prevlatitude[20] = "", prevlongitude[20] = ""; // (recalled with [prev] button) int geomap_position(float flat, float flong, int &mx, int &my); // latitude/longitude > map position int geomap_coordinates(int mx, int my, float &flat, float &flong); // map position > latitude/longitude void geomap_paint_dots(); // paint red dots on map where images located int geotags_choosecity(char *location[2], char *coord[2]); // choose one city from multiple options cchar * web_geocode(char *location[2], char *coord[2]); // find latitude/longitude via web service /**************************************************************************/ // validate and convert earth coordinates, latitude and longitude // return: 0 OK // 1 both are missing ("null" or "") // 2 invalid data // if status is > 0, 0.0 is returned for both values int validate_latlong(char *latitude, char *longitude, float &flati, float &flongi) { if (! latitude || *latitude == 0 || strmatch(latitude,"null")) if (! longitude || *longitude == 0 || strmatch(longitude,"null")) goto status1; // both missing if (! latitude || *latitude == 0 || strmatch(latitude,"null")) goto status2; // one missing if (! longitude || *longitude == 0 || strmatch(longitude,"null")) goto status2; for (int ii = 0; latitude[ii]; ii++) // replace comma decimal point if (latitude[ii] == ',') latitude[ii] = '.'; // with period for (int ii = 0; longitude[ii]; ii++) if (longitude[ii] == ',') longitude[ii] = '.'; flati = atof(latitude); // convert strings to float flongi = atof(longitude); if (flati < -90.0 || flati > +90.0) goto status2; // check range if (flongi < -180.0 || flongi > +180.0) 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: // either missing or invalid flati = flongi = 0.0; return 2; } /**************************************************************************/ // Initialize for geotag functions. // Load geolocations data into memory from image index files. // Returns no. geolocations loaded. int init_geolocs() { int init_glocs_comp(cchar *rec1, cchar *rec2); char city[100], country[100]; char latitude[20], longitude[20]; char *gtags, *pp; float flati, flongi; int err, ftf, cc, ii, jj, keep, Nimages; sxrec_t sxrec; if (Ngeolocs) return Ngeolocs; // already done Ffuncbusy = 1; cc = maxgeolocs * sizeof(geolocs_t); // get memory for geotag locations DB geolocs = (geolocs_t *) zmalloc(cc); // populate geolocs[] from search-index file (= image EXIF data) Nimages = 0; ftf = 1; while (true) { zmainloop(100); if (Ngeolocs == maxgeolocs) { // table full zmessLogACK(Mwin,"max. geotags %d exceeded",maxgeolocs); break; } err = read_sxrec_seq(sxrec,ftf); // read image index recs. if (err) break; gtags = sxrec.gtags; strcpy(city,"null"); strcpy(country,"null"); strcpy(latitude,"null"); strcpy(longitude,"null"); pp = (char *) strField(gtags,'^',1); // city name or "null" if (pp) strncpy0(city,pp,99); pp = (char *) strField(gtags,'^',2); // country if (pp) strncpy0(country,pp,99); pp = (char *) strField(gtags,'^',3); // latitude if (pp) strncpy0(latitude,pp,12); pp = (char *) strField(gtags,'^',4); // longitude if (pp) strncpy0(longitude,pp,12); zfree(sxrec.file); // free sxrec allocations zfree(sxrec.tags); zfree(sxrec.capt); zfree(sxrec.comms); zfree(sxrec.gtags); if (! strmatch(latitude,"null") || ! strmatch(longitude,"null")) { err = validate_latlong(latitude,longitude,flati,flongi); // validate and replace bad data if (err) { strcpy(latitude,"null"); // 1/2 = missing/bad data strcpy(longitude,"null"); continue; } } Nimages++; // images with valid geotags if (Ngeolocs) { ii = Ngeolocs - 1; if (strmatch(city,geolocs[ii].city)) // skip same city in sequence (frequent) if (strmatch(country,geolocs[ii].country)) continue; // to reduce subsequent sort } ii = Ngeolocs++; // fill next entry in table geolocs[ii].city = zstrdup(city); geolocs[ii].country = zstrdup(country); geolocs[ii].lati = zstrdup(latitude); geolocs[ii].longi = zstrdup(longitude); geolocs[ii].flati = atof(latitude); geolocs[ii].flongi = atof(longitude); } cc = sizeof(geolocs_t); HeapSort((char *) geolocs, cc, Ngeolocs, init_glocs_comp); // sort by country, city for (ii = 0, jj = 1; jj < Ngeolocs; jj++) // eliminate duplicate cities { keep = 0; if (! strmatch(geolocs[jj].country,geolocs[ii].country)) keep = 1; if (! strmatch(geolocs[jj].city,geolocs[ii].city)) keep = 1; if (keep) { ii++; geolocs[ii] = geolocs[jj]; } else { zfree(geolocs[jj].country); zfree(geolocs[jj].city); zfree(geolocs[jj].lati); zfree(geolocs[jj].longi); } } Ngeolocs = ii + 1; printz("%d images have %d unique geotags \n",Nimages,Ngeolocs); Ffuncbusy = 0; return Ngeolocs; } // Compare 2 geolocs records by country and city // return <0 0 >0 for rec1 < == > rec2. int init_glocs_comp(cchar *rec1, cchar *rec2) { int ii; char * country1 = ((geolocs_t *) rec1)->country; // compare countries char * country2 = ((geolocs_t *) rec2)->country; ii = strcmp(country1,country2); if (ii) return ii; char * city1 = ((geolocs_t *) rec1)->city; // compare cities char * city2 = ((geolocs_t *) rec2)->city; ii = strcmp(city1,city2); return ii; } /**************************************************************************/ // get latitude/longitude data for a city [ country ] // inputs: location[0] = city // location[1] = country (opt) // outputs: coord[2] = latitude -90 to +90, longitude -180 to +180 // matches[20][2] up to 20 matching city/country locations // returns: no. matches for input city [ country ] // (max. 20) // // use null or "" for missing city or country input (no NULL pointer) // coordinates are returned for the first match only, if any // int get_geolocs(char *location[2], char *coord[2], char *matches[20][2]) { int cc, ii, jj, Nmatch; int fcity = 0, fcountry = 0; if (location[0]) if (*location[0]) fcity = 1; if (location[1]) if (*location[1]) fcountry = 1; if (! fcity && ! fcountry) return 0; // one of these must be present for (ii = Nmatch = 0; ii < Ngeolocs; ii++) // search for exact city [ country ] { if (fcity && ! strmatchcase(location[0],geolocs[ii].city)) continue; if (fcountry && ! strmatchcase(location[1],geolocs[ii].country)) continue; for (jj = 0; jj < Nmatch; jj++) { // look for duplicate match if (strmatch(geolocs[ii].city,matches[jj][0])) // (EXIF and cities-geotag file) if (strmatch(geolocs[ii].country,matches[jj][1])) break; } if (jj < Nmatch) continue; // discard duplicates matches[Nmatch][0] = geolocs[ii].city; matches[Nmatch][1] = geolocs[ii].country; Nmatch++; if (Nmatch == 20) break; // no more than 20 matches are reported if (Nmatch == 1) { coord[0] = geolocs[ii].lati; // return lat/long of 1st match coord[1] = geolocs[ii].longi; StripZeros(coord[0]); StripZeros(coord[1]); } } if (Nmatch) return Nmatch; // exact match found for (ii = Nmatch = 0; ii < Ngeolocs; ii++) // search for partial city [ country ] { if (strmatch("null",geolocs[ii].city)) continue; if (fcity) { cc = strlen(location[0]); if (! strmatchcaseN(location[0],geolocs[ii].city,cc)) continue; } if (fcountry) { cc = strlen(location[1]); if (! strmatchcaseN(location[1],geolocs[ii].country,cc)) continue; } matches[Nmatch][0] = geolocs[ii].city; matches[Nmatch][1] = geolocs[ii].country; Nmatch++; if (Nmatch == 20) break; // no more than 20 matches are reported if (Nmatch == 1) { coord[0] = geolocs[ii].lati; // return geolat/long of 1st match coord[1] = geolocs[ii].longi; StripZeros(coord[0]); StripZeros(coord[1]); } } return Nmatch; } /**************************************************************************/ // Save geotags in image file EXIF and image index file. // Update geolocs[*] memory table // // location[2] = city and country // coord[2] = latitude and longitude // return value: 0 OK, no geotag revision (incomplete data) // 1 OK, no geotag revision (matches existing data) // 2 OK, geotag lat/long updated // 3 OK, geotag new location added // -1 error, lat/long bad int put_geotags(char *location[2], char *coord[2]) { char acoord[2][20]; float flati, flongi; int ii, err, Fnew = 0, Fchange = 0; int retval; if (! curr_file) return 0; err = validate_latlong(coord[0],coord[1],flati,flongi); if (err) { if (err == 2) goto badcoord; // reject bad data strcpy(acoord[0],"null"); // replace missing data with "null" strcpy(acoord[1],"null"); flati = flongi = 0; // lati/longi missing value } else { snprintf(acoord[0],20,"%.4f",flati); // reformat with std. precision snprintf(acoord[1],20,"%.4f",flongi); StripZeros(acoord[0]); StripZeros(acoord[1]); } if (! strmatch(location[0],"null")) // unless null, *location[0] = toupper(*location[0]); // force capitalization if (! strmatch(location[1],"null")) *location[1] = toupper(*location[1]); strncpy0(meta_city,location[0],99); // save geotags in image file EXIF strncpy0(meta_country,location[1],99); // and in search-index file strncpy0(meta_latitude,acoord[0],12); strncpy0(meta_longitude,acoord[1],12); save_filemeta(curr_file); if (! *location[0] || strmatch(location[0],"null")) return 0; // quit here if city data not complete if (! *location[1] || strmatch(location[1],"null")) return 0; for (ii = 0; ii < Ngeolocs; ii++) { // search geotags for city, country if (! strmatchcase(location[0],geolocs[ii].city)) continue; // (case-insensitive compare) if (strmatchcase(location[1],geolocs[ii].country)) break; } if (ii < Ngeolocs) { // found, check for revised lat/long if (! strmatch(geolocs[ii].lati,acoord[0])) Fchange = 1; if (! strmatch(geolocs[ii].longi,acoord[1])) Fchange = 1; if (! strmatch(geolocs[ii].city,location[0])) Fchange = 1; // or revised capitalization if (! strmatch(geolocs[ii].country,location[1])) Fchange = 1; } else Fnew = 1; // a new city, country if (Fnew + Fchange == 0) return 1; // no change if (Fchange) { zfree(geolocs[ii].city); // change geotag data in memory geolocs[ii].city = zstrdup(location[0]); // (to be used subsequently) zfree(geolocs[ii].country); geolocs[ii].country = zstrdup(location[1]); // presence in image EXIF will make zfree(geolocs[ii].lati); // this the preferred version geolocs[ii].lati = zstrdup(acoord[0]); zfree(geolocs[ii].longi); geolocs[ii].longi = zstrdup(acoord[1]); geolocs[ii].flati = flati; geolocs[ii].flongi = flongi; retval = 2; } else if (Fnew) { if (Ngeolocs == maxgeolocs) { zmessLogACK(Mwin,"max. geotags %d exceeded",maxgeolocs); return -1; } for (ii = Ngeolocs; ii > 0; ii--) // shift all geotag data up geolocs[ii] = geolocs[ii-1]; Ngeolocs++; ii = 0; geolocs[ii].city = zstrdup(location[0]); // new geotag is now first geolocs[ii].country = zstrdup(location[1]); // (find again faster) geolocs[ii].lati = zstrdup(acoord[0]); geolocs[ii].longi = zstrdup(acoord[1]); geolocs[ii].flati = flati; geolocs[ii].flongi = flongi; retval = 3; } else return -1; // should not happen return retval; badcoord: zmessageACK(Mwin,ZTX("bad latitude/longitude: %s %s"),coord[0],coord[1]); return -1; } /**************************************************************************/ // Geographic Map Functions // Maps of any scale can be user-installed. // Mercator projection is assumed (but unimportant for maps < 100 km). namespace geomap { char mapname[100]; int mapww, maphh; // map width, height float mlat[2]; // latitude range, low - high float mlong[2]; // longitude range, low - high } // load the default world map or a map chosen by the user void m_load_geomap(GtkWidget *, cchar *menu) { using namespace geomap; int load_geomap_dialog_event(zdialog *zd, cchar *event); char mapindex[200], mapfile[200]; char buff[200]; cchar *pp; zdialog *zd, *zdbusy; int err, zstat, ii; FILE *fid; float lat1, lat2, long1, long2; STATB statb; F1_help_topic = "images_by_map"; if (checkpend("all")) return; // check, no block if (! init_geolocs() ) return; // insure geolocations are loaded if (Fnoindex) { zmessageACK(Mwin,ZTX("-noindex in use, disabled")); return; } snprintf(mapindex,200,"%s/maps_index",maps_dirk); err = stat(mapindex,&statb); if (err) goto nomapsinstalled; if (menu && strmatch(menu,"default")) { strcpy(mapname,"World.jpg"); // load default world map goto load_map; } fid = fopen(mapindex,"r"); // open file with list of avail. maps if (! fid) goto nomapsinstalled; zd = zdialog_new(ZTX("choose map file"),Mwin,Bcancel,null); 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); if (*mapname && Wstate.fpxb) // show current map if any zdialog_stuff(zd,"mapname",mapname); zdialog_resize(zd,300,100); zdialog_run(zd,load_geomap_dialog_event); // 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) goto mapfilemissing; fid = fopen(mapindex,"r"); // read map file list again 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; lat1 = lat2 = long1 = long2 = 0; pp = strField(buff,",",2); // get map latitude/longitude range if (! pp) goto latlongerr; // and verify data OK err = convSF(pp,lat1,-80,+80); if (err) goto latlongerr; pp = strField(buff,",",3); if (! pp) goto latlongerr; err = convSF(pp,lat2,-80,+80); if (err) goto latlongerr; pp = strField(buff,",",4); if (! pp) goto latlongerr; err = convSF(pp,long1,-200,+200); if (err) goto latlongerr; pp = strField(buff,",",5); if (! pp) goto latlongerr; err = convSF(pp,long2,-200,+200); if (err) goto latlongerr; if (lat2 < lat1 + 0.1) goto latlongerr; // require min. 0.1 degree map range if (long2 < long1 + 0.1) goto latlongerr; // (11 km) printz("load geomap: %s \n",mapname); // no errors, commit to load map Ffuncbusy = 1; zdbusy = zmessage_post(Mwin,0,"Loading geomap ...",null); for (ii = 0; ii < 5; ii++) { // required to get message out zmainloop(); // X11 or GDK problem zsleep(0.01); } free_geomap(); // free prior map Wstate.fpxb = PXB_load(mapfile,1); // load map file (with diagnostic) if (! Wstate.fpxb) { Ffuncbusy = 0; return; } mapww = Wstate.fpxb->ww; // save map pixel dimensions maphh = Wstate.fpxb->hh; mlat[0] = lat1; // save map latitude/longitude range mlat[1] = lat2; mlong[0] = long1; mlong[1] = long2; zdialog_free(zdbusy); // kill the message Ffuncbusy = 0; m_zoom(null,"fit"); return; nomapsinstalled: zmessageACK(Mwin,ZTX("fotoxx-maps package not installed \n" "(see http://kornelix.com/packages and /tarballs)")); 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"),lat1,lat2,long1,long2); return; } // dialog event and completion function int load_geomap_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"mapname")) zd->zstat = 1; return 1; } // Convert map position px/py into latitude and longitude. // Return 0 if OK, +N if error (off the map). int geomap_coordinates(int px, int py, float &flat, float &flong) { using namespace geomap; float lat1, lat2, long1, long2; float zww, qy, qy2; flat = flong = 0; if (px < 0 || px > mapww) return 1; // px/py outside map size if (py < 0 || py > maphh) return 1; lat1 = mlat[0]; // map latitude low - high range lat2 = mlat[1]; long1 = mlong[0]; // map longitude low - high range long2 = mlong[1]; flong = long1 + (1.0 * px / mapww) * (long2 - long1); // return longitude zww = mapww * 360.0 / (long2 - long1); // width for -180 to +180 longitude lat1 = lat1 / RAD; // convert to radians lat2 = lat2 / RAD; qy2 = (zww/2/PI) * (log(tan(lat2/2 + PI/4))); // lat2 distance from equator qy2 = qy2 - py; // py distance from equator qy = fabsf(qy2); flat = 2 * atan(exp(2*PI*qy/zww)) - PI/2; if (qy2 < 0) flat = -flat; flat = flat * RAD; // return latitude return 0; } // Convert latitude and longitude into map position px/py. // Return 0 if OK, +N if error (off the map). int geomap_position(float flat, float flong, int &px, int &py) { using namespace geomap; float lat1, lat2, long1, long2; float zww, qy, qy2; lat1 = mlat[0]; // map latitude low - high range lat2 = mlat[1]; long1 = mlong[0]; // map longitude low - high range long2 = mlong[1]; px = py = 0; if (flat < lat1 || flat >= lat2) return 1; // flat/flong outside map limits if (flong < long1 || flong >= long2) return 1; px = (flong - long1) / (long2 - long1) * mapww; // return px position zww = mapww * 360.0 / (long2 - long1); // width for -180 to +180 longitude lat1 = lat1 / RAD; // convert to radians lat2 = lat2 / RAD; flat = flat / RAD; qy2 = (zww/2/PI) * (log(tan(lat2/2 + PI/4))); // lat2 distance from equator qy = (zww/2/PI) * (log(tan(flat/2 + PI/4))); // flat 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; } // paint red dots corresponding to image locations on map void geomap_paint_dots() // 15.01 { int ii, err; int mx, my, dx, dy; float flati, flongi, radius; if (! Wstate.fpxb) return; // no map loaded radius = 3; if (Cstate->mscale >= 1) radius = 4; cairo_set_source_rgb(mwcr,1,0,0); for (ii = 0; ii < Ngeolocs; ii++) // paint red dots on map { // where images are present flati = geolocs[ii].flati; flongi = geolocs[ii].flongi; err = geomap_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(mwcr,dx,dy,radius,0,2*PI); cairo_fill(mwcr); } return; } /**************************************************************************/ // Respond to mouse movement and left clicks on geomap image. // Set longitude and latitude, and city and country. void geomap_mousefunc() { void find_geoloc_images(float flati, float flongi); // private function int err, mx, my, px, py, ii, minii; char *city, *country; float flati, flongi, glati, glongi; float dist, mindist; float mscale = Cstate->mscale; static char *pcity = 0; if (checkpend("edit busy block")) return; // check nothing pending 15.10 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 = geomap_coordinates(mx,my,flati,flongi); if (err) return; // off the map dist = mindist = 999999; minii = 0; for (ii = 0; ii < Ngeolocs; ii++) // find nearest city/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; flati = geolocs[ii].flati; // closest known place flongi = geolocs[ii].flongi; city = geolocs[ii].city; country = geolocs[ii].country; err = geomap_position(flati,flongi,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 > 10) city = country = 0; // > capture distance if (LMclick) // left mouse click { LMclick = 0; poptext_window(0,0,0,0,0,0); // remove popup if (zd_geotags) { zdialog_stuff(zd_geotags,"city",city); // stuff calling dialog zdialog_stuff(zd_geotags,"country",country); zdialog_stuff(zd_geotags,"latitude",flati); zdialog_stuff(zd_geotags,"longitude",flongi); zdialog_send_event(zd_geotags,"geomap"); // activate calling dialog } else if (city) find_geoloc_images(flati,flongi); // show images for location } else if (city) { // mouse movement, no click if (! pcity || ! strmatch(city,pcity)) { poptext_window(city,MWIN,Mwxposn,Mwyposn,0.1,1); // popup the city name at mouse pcity = city; } } else if (pcity) { poptext_window(0,0,0,0,0,0); // remove popup pcity = 0; } return; } /**************************************************************************/ // find images in range of given geolocation, show gallery of images // privat function for geomap_mousefunc(), called when a location is clicked void find_geoloc_images(float flati, float flongi) { int ftf, err, nn = 0; char imagefile[XFCC]; float glati, glongi, grange; cchar *pp; FILE *fid; sxrec_t sxrec; if (Fnoindex) { zmessageACK(Mwin,ZTX("-noindex in use, disabled")); return; } fid = fopen(searchresults_file,"w"); // open output file if (! fid) { zmessLogACK(Mwin,"output file error: %s",strerror(errno)); return; } ftf = 1; while (true) // read image index recs. { err = read_sxrec_seq(sxrec,ftf); if (err) break; strncpy0(imagefile,sxrec.file,XFCC); // save filespec pp = strField(sxrec.gtags,'^',3); // latitude if (! pp) goto freemem; if (strmatch(pp,"null")) goto freemem; glati = atof(pp); pp = strField(sxrec.gtags,'^',4); // longitude if (! pp) goto freemem; if (strmatch(pp,"null")) goto freemem; glongi = atof(pp); grange = (flati - glati) * (flati - glati); grange += (flongi - glongi) * (flongi - glongi); grange = 111.0 * sqrt(grange); if (grange <= geomap_range) { // within distance limit, select fprintf(fid,"%s\n",imagefile); // output matching file nn++; } freemem: zfree(sxrec.file); zfree(sxrec.tags); zfree(sxrec.capt); zfree(sxrec.comms); zfree(sxrec.gtags); } fclose(fid); if (! nn) { zmessageACK(Mwin,ZTX("No matching images found")); return; } free_resources(); navi::gallerytype = 2; // search results gallery(searchresults_file,"initF"); // generate gallery of matching files m_viewmode(0,"G"); gallery(0,"paint",0); // 15.05 return; } /**************************************************************************/ // free memory lorge memory used for geomap image // used by edit_setup() to maximize available memory void free_geomap() { if (Wstate.fpxb) PXB_free(Wstate.fpxb); Wstate.fpxb = 0; return; } /**************************************************************************/ // set the map search range from a clicked position, kilometers void m_mapsearch_range(GtkWidget *, cchar *) { zdialog *zd; int zstat; F1_help_topic = "images_by_map"; zd = zdialog_new(0,Mwin,Bdone,null); // 15.08 zdialog_add_widget(zd,"hbox","hbox","dialog"); zdialog_add_widget(zd,"label","label","hbox",ZTX("search range (km)"),"space=5"); zdialog_add_widget(zd,"spin","range","hbox","1|999|1|10"); zdialog_stuff(zd,"range",geomap_range); zdialog_run(zd); zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"range",geomap_range); zdialog_free(zd); return; } /**************************************************************************/ // add or edit image geotags - city, country, latitude, longitude void m_edit_geotags(GtkWidget *, cchar *menu) { int edit_geotags_dialog_event(zdialog *zd, cchar *event); /** Edit Geotags city [______________] country [______________] latitude [_______] longitude [_______] [find] [web] [prev] [map] [apply] [clear] [cancel] **/ cchar *title = ZTX("Edit Geotags"); cchar *mapquest1 = ZTX("Geocoding web service courtesy of"); cchar *mapquest2 = "http://www.mapquest.com"; zdialog *zd; F1_help_topic = "edit_geotags"; if (! init_geolocs()) return; // initialize geotags if (! curr_file) return; if (! zdeditgeotags) // start dialog if not already { zdeditgeotags = zdialog_new(title,Mwin,Bfind,Bweb,Bprev,Bmap,Bapply,Bclear,Bcancel,null); zd = zdeditgeotags; zdialog_add_ttip(zd,Bapply,ZTX("save metadata to file")); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labcity","hb1",ZTX("city"),"space=5"); zdialog_add_widget(zd,"entry","city","hb1",0,"expand"); zdialog_add_widget(zd,"label","space","hb1",0,"space=5"); zdialog_add_widget(zd,"label","labcountry","hb1",ZTX("country"),"space=5"); zdialog_add_widget(zd,"entry","country","hb1",0,"expand"); zdialog_add_widget(zd,"hbox","hb4","dialog"); zdialog_add_widget(zd,"label","lablat","hb4","Latitude","space=3"); zdialog_add_widget(zd,"entry","latitude","hb4",0,"size=10"); zdialog_add_widget(zd,"label","space","hb4",0,"space=5"); zdialog_add_widget(zd,"label","lablong","hb4","Longitude","space=3"); zdialog_add_widget(zd,"entry","longitude","hb4",0,"size=10"); zdialog_add_widget(zd,"hbox","hbmq","dialog"); zdialog_add_widget(zd,"label","labmq","hbmq",mapquest1,"space=3"); zdialog_add_widget(zd,"link","MapQuest","hbmq",mapquest2); zdialog_run(zd,edit_geotags_dialog_event); } zd = zdeditgeotags; zd_geotags = zd; // activate geomap clicks if (strmatch(meta_city,"null")) zdialog_stuff(zd,"city",""); else zdialog_stuff(zd,"city",meta_city); if (strmatch(meta_country,"null")) zdialog_stuff(zd,"country",""); else zdialog_stuff(zd,"country",meta_country); if (strmatch(meta_latitude,"null")) zdialog_stuff(zd,"latitude",""); else zdialog_stuff(zd,"latitude",meta_latitude); if (strmatch(meta_longitude,"null")) zdialog_stuff(zd,"longitude",""); else zdialog_stuff(zd,"longitude",meta_longitude); return; } // dialog event and completion callback function int edit_geotags_dialog_event(zdialog *zd, cchar *event) { int zstat, Nmatch; char *location[2], *coord[2], *matches[20][2]; char city[100], country[100]; char latitude[20], longitude[20], temp[20]; float fcoord[2]; cchar *errmess; if (strmatch(event,"escape")) zd->zstat = 7; // escape = cancel 15.07 if (! curr_file) return 1; if (strmatch(event,"enter")) zd->zstat = 5; // [apply] if (strmatch(event,"geomap")) { // have geotags data from geomap Fgtagmod++; // dialog inputs changed return 1; } if (strstr("city country latitude longitude",event)) { // dialog inputs changed Fgtagmod++; return 1; } if (! zd->zstat) return 1; // wait for action button zstat = zd->zstat; zd->zstat = 0; // keep dialog active m_viewmode(0,"F"); // back to F view zdialog_fetch(zd,"city",city,99); // get city [country] from dialog zdialog_fetch(zd,"country",country,99); location[0] = city; location[1] = country; zdialog_fetch(zd,"latitude",temp,20); // and latitude, longitude repl_1str(temp,latitude,",","."); // (replace comma decimal) zdialog_fetch(zd,"longitude",temp,20); repl_1str(temp,longitude,",","."); coord[0] = latitude; coord[1] = longitude; if (zstat == 1) // [find] { Nmatch = get_geolocs(location,coord,matches); // find in geolocs[*] table if (Nmatch == 0) // no matches zmessageACK(Mwin,ZTX("city not found")); else if (Nmatch == 1) { // one match zdialog_stuff(zd,"city",matches[0][0]); // stuff matching city data into dialog zdialog_stuff(zd,"country",matches[0][1]); zdialog_stuff(zd,"latitude",coord[0]); zdialog_stuff(zd,"longitude",coord[1]); Fgtagmod++; // dialog inputs changed } else { // multiple matching cities zstat = geotags_choosecity(location,coord); // ask user to choose one if (zstat == 1) { // response is available zdialog_stuff(zd,"city",location[0]); // stuff matching city data into dialog zdialog_stuff(zd,"country",location[1]); zdialog_stuff(zd,"latitude",coord[0]); zdialog_stuff(zd,"longitude",coord[1]); Fgtagmod++; // dialog inputs changed } } return 1; } else if (zstat == 2) // [web] { errmess = web_geocode(location,coord); // look-up in web service if (errmess) zmessageACK(Mwin,errmess); // fail else { zdialog_stuff(zd,"city",location[0]); // success, return all data zdialog_stuff(zd,"country",location[1]); // (location may have been completed) zdialog_stuff(zd,"latitude",coord[0]); zdialog_stuff(zd,"longitude",coord[1]); Fgtagmod++; // dialog inputs changed } return 1; } else if (zstat == 3) // [prev] { zdialog_stuff(zd,"city",prevcity); // get last-used geotags zdialog_stuff(zd,"country",prevcountry); zdialog_stuff(zd,"latitude",prevlatitude); zdialog_stuff(zd,"longitude",prevlongitude); Fgtagmod++; // dialog inputs changed return 1; } else if (zstat == 4) // [map] { m_viewmode(0,"W"); return 1; } else if (zstat == 5) // [apply] { if (! Fgtagmod) return 1; // nothing changed if (strmatch(coord[0],"null")) *coord[0] = 0; // replace "null" with "" if (strmatch(coord[1],"null")) *coord[1] = 0; if (*coord[0] || *coord[1]) { // if coordinates present, validate if (! *coord[0] || ! *coord[1]) goto badcoord; fcoord[0] = atof(coord[0]); if (fcoord[0] < -90 || fcoord[0] > 90) goto badcoord; fcoord[1] = atof(coord[1]); if (fcoord[1] < -180 || fcoord[1] > 180) goto badcoord; if (fcoord[0] == 0 && fcoord[1] == 0) goto badcoord; } put_geotags(location,coord); // update EXIF, image index, geolocs[*] table strcpy(prevcity,city); // save data for later use as [prev] strcpy(prevcountry,country); strcpy(prevlatitude,latitude); strcpy(prevlongitude,longitude); gtk_window_present(MWIN); // keep focus on main window return 1; } else if (zstat == 6) // [clear] { zdialog_stuff(zd,"city",""); // erase dialog fields zdialog_stuff(zd,"country",""); zdialog_stuff(zd,"latitude",""); zdialog_stuff(zd,"longitude",""); Fgtagmod++; return 1; } else { // cancel Fgtagmod = 0; // no changes made 15.03 zdialog_free(zd); zdeditgeotags = 0; zd_geotags = 0; // deactivate geomap clicks m_viewmode(0,"F"); return 1; } return 1; badcoord: zmessageACK(Mwin,ZTX("bad latitude/longitude: %s %s"),coord[0],coord[1]); return 1; } /**************************************************************************/ // batch add geotags - set geotags for multiple image files char **batch_geotags_filelist = 0; int batch_geotags_filecount = 0; void m_batch_geotags(GtkWidget *, cchar *menu) { int batch_geotags_dialog_event(zdialog *zd, cchar *event); cchar *title = ZTX("Batch Add Geotags"); char *file, **flist; zdialog *zd; int ii, err; char *location[2], *coord[2]; char city[100], country[100]; char latitude[20], longitude[20]; cchar *mapquest1 = "Geocoding web service courtesy of"; cchar *mapquest2 = "http://www.mapquest.com"; F1_help_topic = "batch_geotags"; if (! init_geolocs()) return; // initialize geotags if (checkpend("all")) return; // check nothing pending (block below) /** Batch Add Geotags [select files] NN files selected city [______________] country [______________] latitude [_______] longitude [_______] [find] [web] [prev] [map] [proceed] [cancel] **/ zd = zdialog_new(title,Mwin,Bfind,Bweb,Bprev,Bmap,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","labcity","hb2",ZTX("city"),"space=5"); zdialog_add_widget(zd,"entry","city","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,"entry","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,"entry","latitude","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,"entry","longitude","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); batch_geotags_filelist = 0; batch_geotags_filecount = 0; flist = 0; zd_geotags = zd; // activate geomap clicks zdialog_run(zd,batch_geotags_dialog_event); // run dialog zdialog_wait(zd); // wait for dialog completion if (zd->zstat != 5) goto cleanup; // status not [proceed] if (! batch_geotags_filecount) goto cleanup; // no files selected zdialog_fetch(zd,"city",city,99); // get city [country] from dialog zdialog_fetch(zd,"country",country,99); location[0] = city; location[1] = country; zdialog_fetch(zd,"latitude",latitude,20); // and latitude, longitude zdialog_fetch(zd,"longitude",longitude,20); coord[0] = latitude; coord[1] = longitude; zdialog_free(zd); // kill dialog flist = batch_geotags_filelist; // selected files if (! flist) goto cleanup; if (checkpend("all")) goto cleanup; // check nothing pending 15.10 Fblock = 1; write_popup_text("open","Adding Geotags",500,200,Mwin); // status monitor popup window for (ii = 0; flist[ii]; ii++) // loop all selected files { file = flist[ii]; // display image err = f_open(file,0,0,0); if (err) continue; write_popup_text("write",file); // report progress zmainloop(); put_geotags(location,coord); // update file geotags } write_popup_text("write","COMPLETED"); Fblock = 0; cleanup: if (zd) zdialog_free(zd); zd_geotags = 0; // deactivate geomap clicks if (flist) { for (ii = 0; flist[ii]; ii++) zfree(flist[ii]); zfree(flist); } return; } // batch_geotags dialog event function int batch_geotags_dialog_event(zdialog *zd, cchar *event) { int ii, yn, zstat, Nmatch; char **flist = batch_geotags_filelist; char countmess[50]; char *location[2], *coord[2], *matches[20][2]; char city[100], country[100]; char latitude[20], longitude[20], temp[20]; cchar *errmess; float fcoord[2]; if (strmatch(event,"escape")) zd->zstat = 6; // escape = cancel 15.07 if (strmatch(event,"files")) // select images to add tags { if (flist) { // free prior list for (ii = 0; flist[ii]; ii++) zfree(flist[ii]); zfree(flist); } zdialog_show(zd,0); // hide parent dialog flist = gallery_getfiles(); // get file list from user zdialog_show(zd,1); batch_geotags_filelist = flist; if (flist) // count files in list for (ii = 0; flist[ii]; ii++); else ii = 0; batch_geotags_filecount = ii; snprintf(countmess,50,Bfileselected,batch_geotags_filecount); zdialog_stuff(zd,"labcount",countmess); } if (! zd->zstat) return 1; // wait for action button zstat = zd->zstat; zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"city",city,99); // get city [country] from dialog zdialog_fetch(zd,"country",country,99); location[0] = city; location[1] = country; zdialog_fetch(zd,"latitude",temp,20); // and latitude, longitude repl_1str(temp,latitude,",","."); // (replace comma decimal) zdialog_fetch(zd,"longitude",temp,20); repl_1str(temp,longitude,",","."); coord[0] = latitude; coord[1] = longitude; if (zstat == 1) // [find] { Nmatch = get_geolocs(location,coord,matches); // find in geolocs[*] table if (Nmatch == 0) // no matches zmessageACK(Mwin,ZTX("city not found")); else if (Nmatch == 1) { // one match zdialog_stuff(zd,"city",matches[0][0]); // stuff matching city data into dialog zdialog_stuff(zd,"country",matches[0][1]); zdialog_stuff(zd,"latitude",coord[0]); zdialog_stuff(zd,"longitude",coord[1]); } else { // multiple matching cities zstat = geotags_choosecity(location,coord); // ask user to choose one if (zstat == 1) { // response is available zdialog_stuff(zd,"city",location[0]); // stuff matching city data into dialog zdialog_stuff(zd,"country",location[1]); zdialog_stuff(zd,"latitude",coord[0]); zdialog_stuff(zd,"longitude",coord[1]); } } } else if (zstat == 2) // [web] { errmess = web_geocode(location,coord); // look-up in web service if (errmess) zmessageACK(Mwin,errmess); // fail else { zdialog_stuff(zd,"city",location[0]); // success, return all data zdialog_stuff(zd,"country",location[1]); // (location may have been completed) zdialog_stuff(zd,"latitude",coord[0]); zdialog_stuff(zd,"longitude",coord[1]); } } else if (zstat == 3) // [prev] { zdialog_stuff(zd,"city",prevcity); // get last-used geotags zdialog_stuff(zd,"country",prevcountry); zdialog_stuff(zd,"latitude",prevlatitude); zdialog_stuff(zd,"longitude",prevlongitude); } else if (zstat == 4) // [map] m_viewmode(0,"W"); else if (zstat == 5) // [proceed] { if (strmatch(coord[0],"null")) *coord[0] = 0; // replace "null" with "" if (strmatch(coord[1],"null")) *coord[1] = 0; if (*coord[0] || *coord[1]) // if coordinates present, validate { if (! *coord[0] || ! *coord[1]) goto badcoord; fcoord[0] = atof(coord[0]); if (fcoord[0] < -90 || fcoord[0] > 90) goto badcoord; fcoord[1] = atof(coord[1]); if (fcoord[1] < -180 || fcoord[1] > 180) goto badcoord; if (fcoord[0] == 0 && fcoord[1] == 0) goto badcoord; } if (! batch_geotags_filecount) goto nofiles; if (! *city || ! *country || ! *latitude || ! *longitude) { // check data is complete yn = zmessageYN(Mwin,ZTX("data is incomplete \n proceed?")); if (! yn) return 1; } strcpy(prevcity,city); // save data for later use as [prev] strcpy(prevcountry,country); strcpy(prevlatitude,latitude); strcpy(prevlongitude,longitude); zd->zstat = 5; // OK to proceed zdialog_destroy(zd); } else { // cancel zdialog_destroy(zd); zd_geotags = 0; // deactivate geomap clicks } return 1; badcoord: zmessageACK(Mwin,ZTX("bad latitude/longitude: %s %s"),coord[0],coord[1]); return 1; nofiles: zmessageACK(Mwin,Bnofileselected); return 1; } // dialog to choose one city from multiple options // location[2] is input city and optional country // (may be substrings, may have multiple matches in city geotags data) // location[2] is output unique city and country after user choice // coord[2] is output latitude, longitude char *geotags_chosenlocation[2]; char *geotags_chosencoord[2]; int geotags_choosecity(char *location[2], char *coord[2]) { int geotags_choosecity_event(zdialog *zd, cchar *event); char *matches[20][2], text[200]; int Nmatch, ii, zstat; zdialog *zd; Nmatch = get_geolocs(location,coord,matches); // get matching city geotags data if (Nmatch == 0) return 0; // no match if (Nmatch == 1) { // one match, done location[0] = matches[0][0]; location[1] = matches[0][1]; return 1; } zd = zdialog_new(ZTX("choose city"),Mwin,BOK,Bcancel,null); // multiple matches, start dialog zdialog_add_widget(zd,"comboE","cities","dialog",0,"space=5"); for (ii = 0; ii < Nmatch; ii++) { // list matching cities to choose from snprintf(text,200,"%s | %s",matches[ii][0],matches[ii][1]); zdialog_cb_app(zd,"cities",text); // duplicates are removed } zdialog_resize(zd,300,100); zdialog_run(zd,geotags_choosecity_event); // run dialog, wait for completion zstat = zdialog_wait(zd); zdialog_free(zd); if (zstat == 1) { // valid response available location[0] = geotags_chosenlocation[0]; location[1] = geotags_chosenlocation[1]; coord[0] = geotags_chosencoord[0]; coord[1] = geotags_chosencoord[1]; return 1; } return 0; } // dialog event function - get chosen city/country from multiple choices int geotags_choosecity_event(zdialog *zd, cchar *event) { char text[200]; static char city[100], country[100]; char *location[2], *coord[2]; char *matches[20][2]; cchar *pp; int nn; static int ftf = 1; if (strmatch(event,"enter")) zd->zstat = 1; // [OK] if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (ftf) { zdialog_cb_popup(zd,"cities"); // first time, open combo box list ftf = 0; } if (strmatch(event,"cities")) { // OK zdialog_fetch(zd,"cities",text,200); pp = strField(text,'|',1); if (pp) strncpy0(city,pp,99); pp = strField(text,'|',2); if (pp) strncpy0(country,pp,99); strTrim2(city); strTrim2(country); location[0] = city; location[1] = country; nn = get_geolocs(location,coord,matches); // find in city geotags data if (nn) { geotags_chosenlocation[0] = location[0]; // use 1st match if > 1 geotags_chosenlocation[1] = location[1]; geotags_chosencoord[0] = coord[0]; geotags_chosencoord[1] = coord[1]; zd->zstat = 1; } else zd->zstat = 2; // bad status } if (zd->zstat) ftf = 1; return 1; } /**************************************************************************/ // Convert a city [country] to latitude/longitude using the // MapQuest geocoding service. // (incomplete names may be completed with a bad guess) cchar * web_geocode(char *location[2], char *coord[2]) // overhaul 13.05, 13.05.1 { int err; static char latitude[20], longitude[20]; char outfile[200], URI[300]; char *pp1, *pp2, buffer[200]; float flati, flongi; FILE *fid; cchar *notfound = ZTX("not found"); cchar *badinputs = ZTX("city and country required"); cchar *query = "http://open.mapquestapi.com/geocoding/v1/address?" "&key=Fmjtd%7Cluub2qa72d%2C20%3Do5-9u700a" "&maxResults=1" "&outFormat=csv"; *coord[0] = *coord[1] = 0; // null outputs *latitude = *longitude = 0; if (*location[0] < ' ' || *location[1] < ' ') return badinputs; snprintf(outfile,199,"%s/web-data",tempdir); // v.14.11 snprintf(URI,299,"\"%s&location=%s,%s\"",query,location[0],location[1]); 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; pp2 = (char *) strField(pp1,",",7); if (! pp2) return notfound; strncpy0(latitude,pp2,12); pp2 = (char *) strField(pp1,",",8); if (! pp2) return notfound; strncpy0(longitude,pp2,12); err = validate_latlong(latitude,longitude,flati,flongi); if (err) return notfound; pp1 = strchr(latitude,'.'); // keep max. 4 decimal digits if (pp1) *(pp1+5) = 0; pp1 = strchr(longitude,'.'); if (pp1) *(pp1+5) = 0; coord[0] = latitude; coord[1] = longitude; return 0; } /**************************************************************************/ // 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. int ggroups_comp(cchar *rec1, cchar *rec2); void ggroups_click(GtkWidget *widget, int line, int pos); int ggroups_getdays(cchar *date); struct grec_t { // image geotags data char *city, *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 ggroups_groupby, ggroups_daterange; void m_geotag_groups(GtkWidget *, cchar *) { int geotag_groups_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int zstat, ftf, err, cc, cc1, cc2; int iix, iig, newgroup; char country[100], city[100], buff[300], pdate[12]; cchar *pp; sxrec_t sxrec; GtkWidget *textwin; F1_help_topic = "images_by_location"; if (checkpend("all")) return; // check nothing pending if (Fnoindex) { zmessageACK(Mwin,ZTX("-noindex in use, disabled")); return; } /*** Report Geotag Groups (o) Group by country (o) Group by country/city (o) Group by country/city/date Combine within [ xx |-|+] days [proceed] [cancel] ***/ zd = zdialog_new(ZTX("Report Geotag Groups"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"radio","country","dialog",ZTX("Group by country")); zdialog_add_widget(zd,"radio","city","dialog",ZTX("Group by country/city")); zdialog_add_widget(zd,"radio","date","dialog",ZTX("Group by country/city/date")); 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,"spin","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,"city",1); zdialog_stuff(zd,"date",0); zdialog_resize(zd,300,0); zdialog_run(zd,geotag_groups_dialog_event); zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"country",iix); if (iix) ggroups_groupby = 1; // group by country zdialog_fetch(zd,"city",iix); if (iix) ggroups_groupby = 2; // group by country/city zdialog_fetch(zd,"date",iix); if (iix) ggroups_groupby = 3; // group by country/city/date (range) zdialog_fetch(zd,"range",ggroups_daterange); zdialog_free(zd); if (Ngrec) { // free prior memory for (iix = 0; iix < Ngrec; iix++) { if (grec[iix].city) zfree(grec[iix].city); if (grec[iix].country) zfree(grec[iix].country); } zfree(grec); } cc = maximages * sizeof(grec_t); // allocate memory grec = (grec_t *) zmalloc(cc); memset(grec,0,cc); Ngrec = 0; ftf = 1; while (true) { err = read_sxrec_seq(sxrec,ftf); // read image index recs. if (err) break; iix = Ngrec; pp = strField(sxrec.gtags,'^',1); // get city if (pp) grec[iix].city = zstrdup(pp); else grec[iix].city = zstrdup("null"); pp = strField(sxrec.gtags,'^',2); // country if (pp) grec[iix].country = zstrdup(pp); else grec[iix].country = zstrdup("null"); strncpy0(grec[iix].pdate,sxrec.pdate,9); // photo date, truncate to yyyymmdd grec[iix].lodate = ggroups_getdays(sxrec.pdate); // days since 0 CE grec[iix].hidate = grec[iix].lodate; if (++Ngrec == maximages) { zmessageACK(Mwin,"too many image files"); return; } zfree(sxrec.file); zfree(sxrec.tags); zfree(sxrec.capt); zfree(sxrec.comms); zfree(sxrec.gtags); } if (! Ngrec) { zmessageACK(Mwin,"no geotags data found"); return; } if (Ngrec > 1) // sort index by country/city/date HeapSort((char *) grec, sizeof(grec_t), Ngrec, ggroups_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 (ggroups_groupby >= 2) if (! strmatch(grec[iix].city,grec[iig].city)) newgroup = 1; // new city >> new group if group by city if (ggroups_groupby >= 3) if (grec[iix].lodate - grec[iig].hidate > ggroups_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].city = grec[iix].country = 0; // no zfree() } grec[iig].count = 1; // group count = 1 } else { zfree(grec[iix].city); // same group zfree(grec[iix].country); // free memory grec[iix].city = 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 textwin = write_popup_text("open",ZTX("geotag groups"),620,400,Mwin); // write groups to popup window if (ggroups_groupby == 1) // group by country { snprintf(buff,300,"%-30s %5s ","Country","Count"); write_popup_text("writebold",buff); for (iig = 0; iig < Ngrec; iig++) { utf8substring(country,grec[iig].country,0,30); cc1 = 30 + strlen(country) - utf8len(country); snprintf(buff,300,"%-*s %5d ",cc1,country,grec[iig].count); write_popup_text("write",buff); } } if (ggroups_groupby == 2) // group by country/city { snprintf(buff,300,"%-30s %-30s %5s ","Country","City","Count"); write_popup_text("writebold",buff); for (iig = 0; iig < Ngrec; iig++) { utf8substring(country,grec[iig].country,0,30); cc1 = 30 + strlen(country) - utf8len(country); utf8substring(city,grec[iig].city,0,30); cc2 = 30 + strlen(city) - utf8len(city); snprintf(buff,300,"%-*s %-*s %5d ", cc1,country,cc2,city,grec[iig].count); write_popup_text("write",buff); } } if (ggroups_groupby == 3) // group by country/city/date (range) { snprintf(buff,300,"%-30s %-30s %-10s %5s ","Country","City","Date","Count"); write_popup_text("writebold",buff); for (iig = 0; iig < Ngrec; iig++) { utf8substring(country,grec[iig].country,0,30); // get graphic cc for UTF-8 names cc1 = 30 + strlen(country) - utf8len(country); utf8substring(city,grec[iig].city,0,30); cc2 = 30 + strlen(city) - utf8len(city); 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; } snprintf(buff,300,"%-*s %-*s %-10s %5d ", cc1,country,cc2,city,pdate,grec[iig].count); write_popup_text("write",buff); } } write_popup_text("top"); textwidget_set_clickfunc(textwin,ggroups_click); // response function for mouse click return; } // dialog event and completion function int geotag_groups_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 return 1; } // Compare 2 grec records by geotags and date, // return < 0 = 0 > 0 for rec1 < = > rec2. int ggroups_comp(cchar *rec1, cchar *rec2) { 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 * city1 = ((grec_t *) rec1)->city; // compare cities char * city2 = ((grec_t *) rec2)->city; ii = strcmp(city1,city2); if (ii) return ii; int date1 = ((grec_t *) rec1)->lodate; // compare dates int date2 = ((grec_t *) rec2)->lodate; ii = date1 - date2; return ii; } // convert yyyymmdd date into days from 0 C.E. // "null" date returns 999999 (year 2737) int ggroups_getdays(cchar *date) { int CEdays(int year, int mon, int day); int year, month, day; char temp[8]; 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); return CEdays(year,month,day); } // convert year/month/day into days since Jan 1, 0001 (day 0 CE) // year is 0001 to 9999, month is 1-12, day is 1-31 int CEdays(int year, int month, int day) { int montab[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; int elaps; 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/city/date void ggroups_click(GtkWidget *widget, int line, int pos) { int iix, ftf, err, lodate, hidate, datex; cchar *pp; char city[100], country[100]; FILE *fid; sxrec_t sxrec; if (checkpend("all")) return; // check nothing pending textwidget_get_line(widget,line,1); // hilite clicked line iix = line - 1; // clicked grec[iix] if (iix < 0 || iix > Ngrec-1) return; strncpy0(country,grec[iix].country,99); // selected country/city/date range strncpy0(city,grec[iix].city,99); lodate = grec[iix].lodate; hidate = grec[iix].hidate; fid = fopen(searchresults_file,"w"); // open output file if (! fid) goto filerror; ftf = 1; while (true) // read image index recs. { err = read_sxrec_seq(sxrec,ftf); if (err) break; pp = strField(sxrec.gtags,'^',2); if (! pp) pp = "null"; // enable search for "null" if (! strmatch(pp,country)) goto freemem; // no country match if (ggroups_groupby >= 2) { pp = strField(sxrec.gtags,'^',1); if (! pp) pp = "null"; if (! strmatch(pp,city)) goto freemem; // no city match } if (ggroups_groupby == 3) { datex = ggroups_getdays(sxrec.pdate); if (datex < lodate || datex > hidate) goto freemem; // no date match } fprintf(fid,"%s\n",sxrec.file); // output matching file freemem: zfree(sxrec.file); zfree(sxrec.tags); zfree(sxrec.capt); zfree(sxrec.comms); zfree(sxrec.gtags); } fclose(fid); free_resources(); navi::gallerytype = 2; // search results gallery(searchresults_file,"initF"); // generate gallery of matching files m_viewmode(0,"G"); gallery(0,"paint",0); return; filerror: zmessLogACK(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[16] = ""; // search images char searchDateTo[16] = ""; 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 searchCity[100] = ""; // search geotags char searchCountry[100] = ""; char searchLatitude[20] = ""; char searchLongitude[20] = ""; float searchRange = 100; float flatitude = 0, flongitude = 0; int Fscanall, Fscancurr, Fnewset, Faddset, Fremset; int Fdates, Ftext, Ffiles, Ftags, Fstars, Fgtags; int Flastver, Fsearchmeta; int Falltags, Falltext, Fallfiles; int Frepgallery, Frepmeta; int Nsearchkeys = 0; char *searchkeys[5]; // search metadata keys and match data char *searchkeydata[5]; char searchkeyx[8], searchkeydatax[8]; } using namespace search_images; void m_search_images(GtkWidget *, cchar *) // overhauled { void search_searchtags_clickfunc(GtkWidget *widget, int line, int pos); void search_matchtags_clickfunc(GtkWidget *widget, int line, int pos); void search_deftags_clickfunc(GtkWidget *widget, int line, int pos); int searchimages_dialog_event(zdialog*, cchar *event); zdialog *zd; GtkWidget *widget; F1_help_topic = "search_images"; if (checkpend("all")) return; // check nothing pending if (Fnoindex) { zmessageACK(Mwin,ZTX("-noindex in use, disabled")); return; } /*** ___________________________________________________________ | 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 [___________] [___________] (yyyymmdd) | | stars range [__] [__] (o) last version all/any | | search tags [________________________________] (o) (o) | | search text [________________________________] (o) (o) | | search files [________________________________] (o) (o) | | other criteria: [geotags] (*) [other] (*) | | | | 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=5"); zdialog_add_widget(zd,"radio","allimages","hbs1",ZTX("all"),"space=3"); zdialog_add_widget(zd,"radio","currset","hbs1",ZTX("current set only"),"space=5"); zdialog_add_widget(zd,"hbox","hbm1","dialog"); zdialog_add_widget(zd,"label","labs1","hbm1",ZTX("matching images:"),"space=5"); 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=5"); zdialog_add_widget(zd,"radio","repgallery","hbrt",ZTX("gallery"),"space=5"); zdialog_add_widget(zd,"radio","repmeta","hbrt",ZTX("metadata"),"space=5"); 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","labD","vb1",ZTX("date range")); zdialog_add_widget(zd,"label","labS","vb1",ZTX("stars range")); zdialog_add_widget(zd,"label","labT","vb1",ZTX("search tags")); zdialog_add_widget(zd,"label","labT","vb1",ZTX("search text")); zdialog_add_widget(zd,"label","labF","vb1",ZTX("search files")); zdialog_add_widget(zd,"hbox","hbD","vb2",0,"space=1"); zdialog_add_widget(zd,"entry","datefrom","hbD",0,"size=12"); zdialog_add_widget(zd,"entry","dateto","hbD",0,"size=12"); zdialog_add_widget(zd,"label","labD","hbD",ZTX("(yyyymmdd)"),"space=5"); zdialog_add_widget(zd,"hbox","hbS","vb2",0,"space=1"); zdialog_add_widget(zd,"entry","starsfrom","hbS",0,"size=2"); zdialog_add_widget(zd,"entry","starsto","hbS",0,"size=2"); zdialog_add_widget(zd,"check","lastver","hbS",ZTX("last version only"),"space=10"); zdialog_add_widget(zd,"label","space","hbS",0,"expand"); zdialog_add_widget(zd,"label","all-any","hbS",ZTX("all/any"),"space=2"); zdialog_add_widget(zd,"hbox","hbT","vb2",0,"space=1"); zdialog_add_widget(zd,"frame","frameT","hbT",0,"expand"); zdialog_add_widget(zd,"text","searchtags","frameT",0,"expand|wrap"); zdialog_add_widget(zd,"radio","alltags","hbT",0); zdialog_add_widget(zd,"radio","anytags","hbT",0); zdialog_add_widget(zd,"hbox","hbC","vb2",0,"space=1|expand"); zdialog_add_widget(zd,"entry","searchtext","hbC",0,"expand"); zdialog_add_widget(zd,"radio","alltext","hbC",0); zdialog_add_widget(zd,"radio","anytext","hbC",0); zdialog_add_widget(zd,"hbox","hbF","vb2",0,"space=1|expand"); zdialog_add_widget(zd,"entry","searchfiles","hbF",0,"expand"); zdialog_add_widget(zd,"radio","allfiles","hbF",0); zdialog_add_widget(zd,"radio","anyfiles","hbF",0); zdialog_add_widget(zd,"hbox","hbO","dialog","space=3"); zdialog_add_widget(zd,"label","labO1","hbO",ZTX("other criteria"),"space=5"); zdialog_add_widget(zd,"button","geotags","hbO",Bgeotags,"space=8"); zdialog_add_widget(zd,"label","geotags#","hbO","( )"); zdialog_add_widget(zd,"label","space","hbO",0,"space=5"); zdialog_add_widget(zd,"button","other","hbO",ZTX("other"),"space=8"); zdialog_add_widget(zd,"label","other#","hbO","( )"); 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,"entry","entertag","hbnt"); 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"); 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"); widget = zdialog_widget(zd,"searchtags"); // tag widget mouse functions textwidget_set_clickfunc(widget,search_searchtags_clickfunc); widget = zdialog_widget(zd,"matchtags"); textwidget_set_clickfunc(widget,search_matchtags_clickfunc); widget = zdialog_widget(zd,"deftags"); textwidget_set_clickfunc(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_restore_inputs(zd); // preload prior user inputs load_deftags(); // stuff defined tags into dialog deftags_stuff(zd,"ALL"); defcats_stuff(zd); // and defined categories 15.08 zdialog_resize(zd,0,600); // start dialog zdialog_run(zd,searchimages_dialog_event,"save"); zdialog_wait(zd); // wait for dialog completion zdialog_free(zd); return; } // mouse click functions for search tags and defined tags widgets void search_searchtags_clickfunc(GtkWidget *widget, int line, int pos) // search tag clicked { char *txline, *txtag, end = 0; txline = textwidget_get_line(widget,line,0); if (! txline) return; txtag = textwidget_get_word(txline,pos,",;:",end); if (! txtag) { zfree(txline); return; } del_tag(txtag,searchtags); // remove from search list zdialog_stuff(zdsearchimages,"searchtags",searchtags); zfree(txline); zfree(txtag); return; } void search_matchtags_clickfunc(GtkWidget *widget, int line, int pos) // matching tag was clicked 15.07 { char *txline, *txtag, end = 0; txline = textwidget_get_line(widget,line,0); if (! txline) return; txtag = textwidget_get_word(txline,pos,",;",end); if (! txtag) { zfree(txline); 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(txline); zfree(txtag); return; } void search_deftags_clickfunc(GtkWidget *widget, int line, int pos) // defined tag clicked { char *txline, *txtag, end = 0; txline = textwidget_get_line(widget,line,0); if (! txline) return; txtag = textwidget_get_word(txline,pos,",;:",end); if (! txtag || end == ':') { zfree(txline); return; } // tag category clicked, ignore 15.05 add_tag(txtag,searchtags,tagScc); // add to search tag list zdialog_stuff(zdsearchimages,"searchtags",searchtags); zfree(txline); zfree(txtag); return; } // search images dialog event and completion callback function int searchimages_dialog_event(zdialog *zd, cchar *event) // overhauled { using namespace navi; int searchimages_select(sxrec_t &sxrec); int searchimages_geotags_dialog(zdialog *zd); int searchimages_metadata_dialog(zdialog *zd); int searchimages_metadata_report(); cchar dateLoDefault[16] = "000001010000"; // date: 0000/01/01 time: 00:00 cchar dateHiDefault[16] = "209912312359"; // date: 2099/12/31 time: 23:59 char nowdatetime[16]; char *file; char **flist, *pp, buffer[XFCC]; int ftf, match, ii, jj, cc, err; int nt, cc1, cc2, ff; int Nadded, Nremoved, Nleft, Npver; sxrec_t sxrec; FILE *fid; char *pp1, *pp2; char entertag[tagcc], matchtags[20][tagcc]; char matchtagstext[(tagcc+2)*20]; char catgname[tagcc]; if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 if (strmatch(event,"geotags")) // get geotags search criteria searchimages_geotags_dialog(zd); if (strmatch(event,"other")) // get other metadata criteria searchimages_metadata_dialog(zd); Fgtags = 0; if (*searchCity || *searchCountry || // search geotags was given *searchLatitude || *searchLongitude) Fgtags = 1; Fsearchmeta = 0; if (Nsearchkeys) Fsearchmeta = 1; // search other metadata was given if (Fgtags) zdialog_stuff(zd,"geotags#","(*)"); // add (*) visual flags to main dialog if else zdialog_stuff(zd,"geotags#","( )"); // geotags or metadata selection active if (Fsearchmeta) zdialog_stuff(zd,"other#","(*)"); else zdialog_stuff(zd,"other#","( )"); 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,"starsfrom",""); zdialog_stuff(zd,"starsto",""); zdialog_stuff(zd,"searchtags",""); zdialog_stuff(zd,"searchtext",""); zdialog_stuff(zd,"searchfiles",""); zdialog_stuff(zd,"geotags#","( )"); Fgtags = 0; *searchCity = *searchCountry = 0; *searchLatitude = *searchLongitude = 0; flatitude = flongitude = 0; zdialog_stuff(zd,"other#","( )"); Fsearchmeta = 0; Nsearchkeys = 0; zd->zstat = 0; // keep dialog active return 1; } if (strmatch(event,"entertag")) // new tag is being typed in 15.07 { 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,"enter")) // KB Enter, tag finished 15.07 { zdialog_fetch(zd,"entertag",entertag,tagcc); // get finished tag cc1 = strlen(entertag); if (! cc1) return 1; if (entertag[cc1-1] == '\n') { // remove newline character cc1--; entertag[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(entertag,pp1,cc1)) { // entered tag matches deftag strncpy(entertag,pp1,cc1); // use deftag upper/lower case ff = 1; break; } } if (ff) break; } if (! ff) { zmessageACK(Mwin,ZTX("not a defined tag: %s"),entertag); return 1; } add_tag(entertag,searchtags,tagScc); // add to search tag list zdialog_stuff(zd,"entertag",""); // update dialog widgets zdialog_stuff(zd,"searchtags",searchtags); zdialog_stuff(zd,"matchtags",""); zdialog_goto(zd,"entertag"); // put focus back on entertag widget return 1; } if (strmatch(event,"defcats")) { // new tag category selection 15.08 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. perform the search. 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,15); // get search date range zdialog_fetch(zd,"dateto",searchDateTo,15); 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,"alltags",Falltags); // get match all/any options zdialog_fetch(zd,"alltext",Falltext); zdialog_fetch(zd,"allfiles",Fallfiles); Fdates = 0; if (*searchDateFrom) Fdates++; // search date from was given else strcpy(searchDateFrom,"000001010000"); // else search from begining of time if (*searchDateTo) Fdates++; // search date to was given else strcpy(searchDateTo,"209912312359"); // else search to end of time if (Fdates) { // complete partial date/time data cc = strlen(searchDateFrom); for (ii = cc; ii < 12; ii++) // default date from: searchDateFrom[ii] = dateLoDefault[ii]; // date: 0000/01/01 time: 00:00 cc = strlen(searchDateTo); for (ii = cc; ii < 12; ii++) // default date to: searchDateTo[ii] = dateHiDefault[ii]; // date: 9999/12/31 time: 23:59 ff = 0; // check search dates reasonable 15.08 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) compact_time(time(0),nowdatetime); // get NOW date/time if (strcmp(searchDateFrom,nowdatetime) >= 0) ff = 1; // search from date >= NOW 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; } } 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 if (Ffiles) strToLower(searchfiles); // all comparisons in lower case if (Ftags) strToLower(searchtags); if (Ftext) strToLower(searchtext); 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; ftf = 1; while (true) { zmainloop(20); err = read_sxrec_seq(sxrec,ftf); // scan all index recs. if (err) break; match = searchimages_select(sxrec); // test against select criteria if (match) { // all criteria passed Nadded++; // count matches fprintf(fid,"%s\n",sxrec.file); // save matching filename } zfree(sxrec.file); // free allocated strings zfree(sxrec.tags); zfree(sxrec.capt); zfree(sxrec.comms); zfree(sxrec.gtags); } 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,"find",ii); if (! file) break; if (*file != '!') { // skip directories fprintf(fid,"%s\n",file); // add image files to output Nleft++; } zfree(file); // free memory } ftf = 1; while (true) { zmainloop(20); err = read_sxrec_seq(sxrec,ftf); // scan all index recs. if (err) break; match = searchimages_select(sxrec); // test against select criteria if (match) { // all criteria passed Nadded++; // count matches fprintf(fid,"%s\n",sxrec.file); // save matching filename } zfree(sxrec.file); // free memory zfree(sxrec.tags); zfree(sxrec.capt); zfree(sxrec.comms); zfree(sxrec.gtags); } 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(20); file = gallery(0,"find",ii); if (! file) break; if (*file == '!') { // skip directories zfree(file); continue; } err = get_sxrec(sxrec,file); if (err) { // no metadata rec? zfree(file); continue; } match = searchimages_select(sxrec); // 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 zfree(sxrec.file); zfree(sxrec.tags); zfree(sxrec.capt); zfree(sxrec.comms); zfree(sxrec.gtags); } 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(20); file = gallery(0,"find",ii); if (! file) break; if (*file == '!') { // skip directories zfree(file); continue; } err = get_sxrec(sxrec,file); if (err) { // no metadata rec? zfree(file); continue; } match = searchimages_select(sxrec); // test against select criteria if (! match) { // failed Nleft++; fprintf(fid,"%s\n",file); // save retained filename } else Nremoved++; zfree(file); // free memory zfree(sxrec.file); zfree(sxrec.tags); zfree(sxrec.capt); zfree(sxrec.comms); zfree(sxrec.gtags); } fclose(fid); } if (Flastver) // 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 (Nadded) Nadded -= Npver; // update counts Nremoved += Npver; Nleft -= Npver; } Ffuncbusy = 0; Fblock = 0; 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 15.07 return 1; } free_resources(); navi::gallerytype = 2; // search results gallery(searchresults_file,"initF"); // generate gallery of matching files if (Frepmeta) // metadata report format searchimages_metadata_report(); m_viewmode(0,"G"); gallery(0,"paint",0); // position at top return 1; filerror: zmessLogACK(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(sxrec_t &sxrec) { int searchimages_geotags_select(char *gtagsrec); int searchimages_metadata_select(char *imagefile); 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; if (strcasestr(sxrec.file,pps)) 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 (strcmp(sxrec.pdate,searchDateFrom) < 0) return 0; if (strcmp(sxrec.pdate,searchDateTo) > 0) return 0; } if (Ftags) // tags match is wanted { Nmatch = Nnomatch = 0; strToLower(sxrec.tags); for (iis = 1; ; iis++) // step thru search tags { pps = strField(searchtags,",;",iis); // (delimited, wildcards) if (! pps) break; if (*pps == ' ') continue; for (iif = 1; ; iif++) // step thru file tags (delimited) { ppf = strField(sxrec.tags,",;",iif); if (! ppf) { Nnomatch++; break; } // count matches and fails if (*ppf == ' ') continue; if (MatchWild(pps,ppf) == 0) { Nmatch++; break; } // wildcard 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 && sxrec.rating[0] < *searchStarsFrom) return 0; if (*searchStarsTo && sxrec.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; if (strcasestr(sxrec.capt,pps)) Nmatch++; // search captions for word else if (strcasestr(sxrec.comms,pps)) Nmatch++; // search comments for word else Nnomatch++; } if (Nmatch == 0) return 0; // no match to any word if (Falltext && Nnomatch) return 0; // no match to all words } if (Fgtags) // geotags match is wanted if (! searchimages_geotags_select(sxrec.gtags)) return 0; if (Fsearchmeta) // other metadata match if (! searchimages_metadata_select(sxrec.file)) return 0; return 1; } /**************************************************************************/ // dialog to get geotags search criteria int searchimages_geotags_dialog(zdialog *zdp) { int searchimages_geotags_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int zstat; /*** Add Geotags Search Criteria city [__________] country [__________] latitude [_______] longitude [_______] [find] [map] range (km) [___] [clear] [apply] [cancel] ***/ if (! init_geolocs()) return 0; zd = zdialog_new(ZTX("Add Geotags Search Criteria"),Mwin,Bclear,Bapply,Bcancel,null); zdialog_add_widget(zd,"hbox","hbg1","dialog",0); zdialog_add_widget(zd,"label","labcity","hbg1",ZTX("city"),"space=3"); zdialog_add_widget(zd,"entry","city","hbg1",0,"expand"); zdialog_add_widget(zd,"label","space","hbg1",0,"space=5"); zdialog_add_widget(zd,"label","labcountry","hbg1",ZTX("country"),"space=3"); zdialog_add_widget(zd,"entry","country","hbg1",0,"expand"); zdialog_add_widget(zd,"hbox","hbg2","dialog",0); zdialog_add_widget(zd,"label","lablat","hbg2","latitude","space=3"); zdialog_add_widget(zd,"entry","latitude","hbg2",0,"size=10"); zdialog_add_widget(zd,"label","space","hbg2",0,"space=5"); zdialog_add_widget(zd,"label","lablong","hbg2","longitude","space=3"); zdialog_add_widget(zd,"entry","longitude","hbg2",0,"size=10"); zdialog_add_widget(zd,"hbox","hbg3","dialog",0); zdialog_add_widget(zd,"button","find","hbg3",Bfind,"space=10"); zdialog_add_widget(zd,"button","map","hbg3",Bmap,"space=10"); zdialog_add_widget(zd,"label","labkm","hbg3",ZTX("range (km)"),"space=10"); zdialog_add_widget(zd,"entry","range","hbg3","100","size=5"); zdialog_show(zdp,0); // hide parent dialog zdialog_restore_inputs(zd); // preload prior user inputs zd_geotags = zd; // activate geomap clicks zdialog_run(zd,searchimages_geotags_dialog_event); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); zd_geotags = 0; // deactivate geomap clicks zdialog_show(zdp,1); // restore parent dialog if (zstat == 1) return 1; // search criteria were entered else return 0; // not } // dialog event and completion function int searchimages_geotags_dialog_event(zdialog *zd, cchar *event) { int fgtags, err, nn; char city[100], country[100]; char *location[2], *coord[2]; if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 if (strmatch(event,"find")) { // get geotags data from zdialog_fetch(zd,"city",city,99); // (partial) city [country] names zdialog_fetch(zd,"country",country,99); location[0] = city; location[1] = country; nn = geotags_choosecity(location,coord); // find matching city geotags data if (nn == 1) { // and ask user to choose zdialog_stuff(zd,"city",location[0]); zdialog_stuff(zd,"country",location[1]); zdialog_stuff(zd,"latitude",coord[0]); zdialog_stuff(zd,"longitude",coord[1]); } } if (strmatch(event,"map")) { // [map] m_viewmode(0,"W"); return 0; } if (strmatch(event,"enter")) zd->zstat = 2; // [apply] if (! zd->zstat) return 0; // wait for completion if (zd->zstat == 1) { zdialog_stuff(zd,"city",""); // clear zdialog_stuff(zd,"country",""); zdialog_stuff(zd,"latitude",""); zdialog_stuff(zd,"longitude",""); zd->zstat = 0; // keep dialog active return 0; } if (zd->zstat != 2) { // cancel *searchCity = *searchCountry = 0; // clear criteria *searchLatitude = *searchLongitude = 0; zd_geotags = 0; // deactivate geomap clicks return 0; } zdialog_fetch(zd,"city",searchCity,99); // apply - get search geotags zdialog_fetch(zd,"country",searchCountry,99); zdialog_fetch(zd,"latitude",searchLatitude,20); zdialog_fetch(zd,"longitude",searchLongitude,20); zdialog_fetch(zd,"range",searchRange); if (*searchCity || *searchCountry) // if city or country supplied, if (strmatch(searchLatitude,"null")) // remove "null" lati/longi search *searchLatitude = *searchLongitude = 0; strToLower(searchCity); strToLower(searchCountry); err = 0; if (*searchLatitude || *searchLongitude) { // sanity check for latitude/longitude if (! *searchLatitude || ! *searchLongitude) err++; flatitude = atof(searchLatitude); flongitude = atof(searchLongitude); if (flatitude < -90 || flatitude > 90) err++; if (flongitude < -180 || flongitude > 180) err++; if (searchRange < 1 || searchRange > 1000) err++; if (strmatch(searchLatitude,"null")) err = 0; // search for null lat/long } if (err) { zmessageACK(Mwin,ZTX("error in latitude/longitude/range")); zd->zstat = 0; return 1; } fgtags = 0; if (! blank_null(searchCity)) fgtags = 1; // look for inputs if (! blank_null(searchCountry)) fgtags = 1; if (! blank_null(searchLatitude)) fgtags = 1; if (! blank_null(searchLongitude)) fgtags = 1; if (fgtags) zd->zstat = 1; // search criteria was given else zd->zstat = 2; // no inputs were made return 1; } // test the image geotags data against the search criteria int searchimages_geotags_select(char *gtags) { float glatitude, glongitude, distance; cchar *pps; if (*searchLatitude) { // if latitude/longitude present, pps = strField(gtags,'^',3); // ignore city/country data if (! pps) return 0; if (strmatch(pps,"null")) // no latitude/longitude data if (flatitude == 0 && flongitude == 0) return 1; // "no data" was wanted glatitude = atof(pps); pps = strField(gtags,'^',4); if (! pps) return 0; if (strmatch(pps,"null")) return 0; glongitude = atof(pps); distance = (flatitude - glatitude) * (flatitude - glatitude); distance += (flongitude - glongitude) * (flongitude - glongitude); distance = 111.0 * sqrt(distance); if (distance <= searchRange) return 1; // within distance limit, match return 0; // outside, no match } if (*searchCity) { // check city and country criteria pps = strField(gtags,'^',1); if (pps && strcasestr(pps,searchCity) == 0) return 0; } if (*searchCountry) { pps = strField(gtags,'^',2); if (pps && strcasestr(pps,searchCountry) == 0) return 0; } return 1; } /**************************************************************************/ // dialog to get metadata search criteria int searchimages_metadata_dialog(zdialog *zdp) { 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 zstat; /*** Search and Report Metadata These items are always reported: date, stars, tags, caption, comment Additional Items for Report Keyword Match Criteria [__________] [__________________] [__________] [__________________] [__________] [__________________] [__________] [__________________] [__________] [__________________] [clear] [apply] [cancel] ***/ zd = zdialog_new("Search and Report Metadata",Mwin,Bclear,Bapply,Bcancel,null); zdialog_add_widget(zd,"label","labmeta","dialog",metamess,"space=3"); 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"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=5|expand"); zdialog_add_widget(zd,"label","lab1","vb1",ZTX("Keyword")); zdialog_add_widget(zd,"entry","key0","vb1"); zdialog_add_widget(zd,"entry","key1","vb1"); zdialog_add_widget(zd,"entry","key2","vb1"); zdialog_add_widget(zd,"entry","key3","vb1"); zdialog_add_widget(zd,"entry","key4","vb1"); zdialog_add_widget(zd,"label","lab2","vb2",ZTX("Match Criteria")); zdialog_add_widget(zd,"entry","match0","vb2",0,"expand"); zdialog_add_widget(zd,"entry","match1","vb2",0,"expand"); zdialog_add_widget(zd,"entry","match2","vb2",0,"expand"); zdialog_add_widget(zd,"entry","match3","vb2",0,"expand"); zdialog_add_widget(zd,"entry","match4","vb2",0,"expand"); strcpy(searchkeyx,"keyx"); strcpy(searchkeydatax,"matchx"); if (! searchkeys[0]) // first call initialization { for (int ii = 0; ii < 5; ii++) { searchkeys[ii] = (char *) zmalloc(40); searchkeydata[ii] = (char *) zmalloc(100); *searchkeys[ii] = *searchkeydata[ii] = 0; } } zdialog_show(zdp,0); // hide parent dialog zdialog_resize(zd,400,300); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,searchimages_metadata_dialog_event); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); zdialog_show(zdp,1); // restore parent dialog if (zstat == 1) return 1; // search criteria were entered else return 0; // not } // dialog event and completion callback function int searchimages_metadata_dialog_event(zdialog *zd, cchar *event) { int ii, jj; char keyx[8] = "keyx", matchx[8] = "matchx"; if (strmatch(event,"enter")) zd->zstat = 2; // [apply] if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) { for (ii = 0; ii < 5; ii++) { // clear keyx[3] = '0' + ii; matchx[5] = '0' + ii; zdialog_stuff(zd,keyx,""); zdialog_stuff(zd,matchx,""); zd->zstat = 0; // keep dialog active } return 0; } if (zd->zstat != 2) { Nsearchkeys = 0; // no search keys return 1; } Nsearchkeys = 0; // apply for (ii = jj = 0; ii < 5; ii++) // get metadata keys { searchkeyx[3] = '0' + ii; zdialog_fetch(zd,searchkeyx,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 searchkeydatax[5] = '0' + ii; zdialog_fetch(zd,searchkeydatax,searchkeydata[ii],100); // get corresp. match value if any strTrim2(searchkeydata[jj],searchkeydata[ii]); // trim leading and trailing blanks if (ii > jj) *searchkeys[ii] = *searchkeydata[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 metadata against metadata select criteria int searchimages_metadata_select(char *file) { char *kvals[5]; int ii, nth; cchar *pp; exif_get(file,(cchar **) searchkeys,kvals,Nsearchkeys); // get the image metadata for (ii = 0; ii < Nsearchkeys; ii++) { // loop all metadata search keys if (*searchkeydata[ii] > ' ') { // key match value(s) are present if (kvals[ii]) { // key values present in metadata for (nth = 1; ; nth++) { // loop all match values pp = strField(searchkeydata[ii],' ',nth); // get each match value if (! pp) return 0; // no more, no match found if (strcasestr(kvals[ii],pp)) break; // found match (substring also) } } else return 0; // empty metadata, no match } } return 1; } // Report the 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 (! mdlist) { cc = nfiles * sizeof(char *); // allocate metadata list mdlist = (char **) zmalloc(cc); // nfiles = curr. gallery files memset(mdlist,0,cc); // mdlist = corresp. metadata list } // (zfree() in image_navigate) 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 mdrows = Nkeys - 1; // report rows (city/country 1 row) for (ii = 0; ii < nfiles; ii++) // loop image gallery files { file = gallery(0,"find",ii); if (! file) continue; exif_get(file,keys,kvals,Nkeys); // get the metadata snprintf(text2,200,"%s %s",kvals[3],kvals[4]); // combine city 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 snprintf(text2,200,"key: %s value: %s \n",keys[jj], kvals[jj]); strcpy(text1+cc,text2); cc += strlen(text2); } if (mdlist[ii]) zfree(mdlist[ii]); // attach metadata for gallery paint mdlist[ii] = zstrdup(text1); for (jj = 0; jj < Nkeys; jj++) // free memory if (kvals[jj]) zfree(kvals[jj]); zfree(file); } gallerytype = 3; // gallery type = search results/metadata return 0; } /************************************************************************** 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 20 keynames may be requested per call // returns: 0 = OK, +N = error int exif_get(cchar *file, cchar **keys, char **kdata, int nkeys) { char *pp; char *inputs[30], *outputs[99]; int cc, ii, jj, err; if (nkeys < 1 || nkeys > 20) 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 *) "-fast2"; // -fast2 reinstated jj = 4; for (ii = 0; ii < nkeys; ii++) // build exiftool inputs { cc = strlen(keys[ii]); // -keyname inputs[jj] = (char *) zmalloc(cc+2); inputs[jj][0] = '-'; strcpy(inputs[jj]+1,keys[ii]); 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++) { uint cc = strlen(keys[jj]); // look for matching input keyname if (! strmatchcaseN(pp,keys[jj],cc)) continue; if (strlen(pp) > cc+2) // if not empty, kdata[jj] = zstrdup(pp+cc+2); // return keyvalue alone } zfree(pp); } return 0; } /**************************************************************************/ // create or change EXIF/IPTC metadata for given image file and key(s) // up to 20 keys may be processed // 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[30]; if (nkeys < 1 || nkeys > 20) zappcrash("exif_put nkeys: %d",nkeys); 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 { cc = strlen(keys[ii]) + strlen(kdata[ii]) + 3; inputs[jj] = (char *) zmalloc(cc); inputs[jj][0] = '-'; // -keyname=value 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, up to 20 keys may be replaced with new values // exiftool -m -tagsfromfile file1 -all -xmp -icc_profile [-keyname=newvalue ...] // file2 -overwrite_original int exif_copy(cchar *file1, cchar *file2, cchar **keys, cchar **kdata, int nkeys) { char *inputs[30]; // revised int cc, ii, jj; if (nkeys > 20) zappcrash("exif_copy() nkeys %d",nkeys); 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 *) "-icc_profile"; // -icc_profile jj = 6; // count of inputs so far for (int ii = 0; ii < nkeys; ii++) // -keyname=keyvalue { 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 = 6; ii < jj-1; 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). ***************************************************************************/ int exif_server(int Nrecs, char **inputs, char **outputs) // revised API { static int fcf = 1; // first call flag 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 (! *input_file) // exif_server input file snprintf(input_file,99,"%s/exiftool_input",tempdir); if (! Nrecs) // kill exiftool process { // (also orphaned process) fid1 = fopen(input_file,"a"); if (fid1) { fprintf(fid1,"-stay_open\nFalse\n"); // tell it to exit fclose(fid1); // bugfix: fflush after fclose } remove(input_file); if (fid2) pclose(fid2); fid2 = 0; fcf = 1; // start exiftool process if called again return 0; } if (fcf) // first call only { 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",input_file); 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; } gallery_monitor("stop"); // stop excess gallery inits 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 } gallery_monitor("start"); 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 records ***************************************************************************/ // Initialize for reading/writing to the image index. // Build a memory map of the first image file in each image index subfile. // Set refresh = 1 to initialize after an index file has been split // or the first image file in an image index file has changed. // Returns 0 if OK, +N otherwise. // // /.../.fotoxx/image_index/index_001 // /.../.fotoxx/image_index/index_002 // /.../.fotoxx/image_index/index_... up to 999 // // Each of these files contains up to 'image_index_max' image filespecs. char **image_index_map = 0; // 1st image file in each index file int init_image_index(int refresh) { int ii, cc; char *pp; char indexfile[200]; char buff[indexrecl]; FILE *fid; if (image_index_map && ! refresh) return 0; // already initialized if (image_index_map) { // free memory for (ii = 0; image_index_map[ii]; ii++) zfree(image_index_map[ii]); zfree(image_index_map); } cc = 1001 * sizeof(char *); // allocate memory image_index_map = (char **) zmalloc(cc); memset(image_index_map,0,cc); for (ii = 0; ii < 999; ii++) // read image index files 001 to 999 { snprintf(indexfile,200,"%s/index_%03d",index_dirk,ii+1); // image_index_map[ii] is for fid = fopen(indexfile,"r"); // image_index_file_(ii+1) if (! fid) break; pp = fgets_trim(buff,indexrecl,fid); if (pp && strmatchN(pp,"file: ",6)) pp = zstrdup(pp+6); // map first image file else pp = zstrdup("empty"); // if none, map "empty" image_index_map[ii] = pp; fclose(fid); } image_index_map[ii] = 0; // mark EOL if (ii == 0) { zmessLogACK(Mwin,ZTX("image index is missing")); m_quit(0,0); } if (ii == 999) { zmessLogACK(Mwin,"too many image index files"); m_quit(0,0); } return 0; } // Return which image index file a given image file belongs in. int find_image_index_file(cchar *file) { char *pp; int ii, nn; init_image_index(0); for (ii = 0; (pp = image_index_map[ii]); ii++) { if (strmatch(pp,"empty")) continue; // same sort as image_index_compare() nn = strcasecmp(file,pp); if (nn == 0) nn = strcmp(file,pp); if (nn < 0) break; // file < 1st index file, go back 1 } if (ii == 0) ii = 1; // file < 1st file in 1st index file while (ii > 1 && strmatch(image_index_map[ii-1],"empty")) ii--; return ii; // (map ii is for index file ii+1) } /**************************************************************************/ // Split an index file when the number of entries > image_index_max // All following index files are renumbered +1. // Index_index file is refreshed. // Returns 0 if OK, else +N. int split_image_index_file(int Nth) { int ii, err, nn, last, filecount; char indexfile1[200], indexfile2[200], indexfile3[200]; char buff[indexrecl], *pp; FILE *fid1 = 0, *fid2 = 0, *fid3 = 0; STATB statb; if (sxrec_fid) { // stop get_sxrec() fclose(sxrec_fid); sxrec_fid = 0; } for (last = Nth; last < 999; last++) // find last index file { snprintf(indexfile1,200,"%s/index_%03d",index_dirk,last); // /.../.fotoxx/image_index/index_NNN err = stat(indexfile1,&statb); if (err) break; } last--; if (last == 999) { zmessLogACK(Mwin,"too many image index files"); return 1; } for (ii = last; ii > Nth; ii--) // rename files following Nth { // index_012 >> index_013 etc. snprintf(indexfile1,200,"%s/index_%03d",index_dirk,ii); snprintf(indexfile2,200,"%s/index_%03d",index_dirk,ii+1); err = rename(indexfile1,indexfile2); if (err) goto file_err; } snprintf(indexfile1,200,"%s/index_%03d",index_dirk,Nth); // read Nth file snprintf(indexfile2,200,"%s/index_%03d_temp2",index_dirk,Nth); // write 2 temp. files snprintf(indexfile3,200,"%s/index_%03d_temp3",index_dirk,Nth); fid1 = fopen(indexfile1,"r"); if (! fid1) goto file_err; fid2 = fopen(indexfile2,"w"); if (! fid2) goto file_err; fid3 = fopen(indexfile3,"w"); if (! fid3) goto file_err; filecount = 0; while (true) // copy half the entries { // to temp2 file pp = fgets_trim(buff,indexrecl,fid1); if (! pp) break; if (strmatchN(pp,"file: ",6)) filecount++; if (filecount > image_index_max/2) break; nn = fprintf(fid2,"%s\n",pp); if (! nn) goto file_err; } if (pp) { nn = fprintf(fid3,"%s\n",pp); // copy remaining record sets if (! nn) goto file_err; // to temp3 file } while (true) { pp = fgets_trim(buff,indexrecl,fid1); if (! pp) break; nn = fprintf(fid3,"%s\n",pp); if (! nn) goto file_err; } err = fclose(fid1); err = fclose(fid2); err = fclose(fid3); fid1 = fid2 = fid3 = 0; if (err) goto file_err; err = rename(indexfile2,indexfile1); // temp2 file replaces Nth index file if (err) goto file_err; snprintf(indexfile1,200,"%s/index_%03d",index_dirk,Nth+1); // temp3 file >> Nth+1 index file err = rename(indexfile3,indexfile1); if (err) goto file_err; err = init_image_index(1); // update image index map return err; file_err: zmessLogACK(Mwin,"split_image_index error \n %s",strerror(errno)); if (fid1) fclose(fid1); if (fid2) fclose(fid2); if (fid3) fclose(fid3); return 3; } /**************************************************************************/ // Get the image index record for given image file. // Returns 0 if OK, 1 if not found, >1 if error (diagnosed). // Returned sxrec_t data has allocated fields subject to zfree(). // // Index file is kept open across calls to reduce overhead for the // normal case of records being accessed in image file sequence // (gallery paint). Random access works, but is slower. int get_sxrec(sxrec_t &sxrec, cchar *file) { FILE *fid = 0; char indexfile[200]; static char buff[indexrecl]; static char pfile[XFCC]; int err, Nth, nn = 0, Fcontinue; cchar *pp, *pp2; static int pNth, logerr = 0; STATB statb; memset(&sxrec,0,sizeof(sxrec)); // clear output record err = stat(file,&statb); // check image file exists if (err) return 1; Fcontinue = 1; // test if prior search can continue if (! sxrec_fid) Fcontinue = 0; // no prior search still open Nth = find_image_index_file(file); // get index file for this image file if (Nth != pNth) Fcontinue = 0; // index file not the same if (image_fcomp(file,pfile) <= 0) Fcontinue = 0; // gallery file sequence if (Fcontinue) fid = sxrec_fid; else { if (sxrec_fid) fclose(sxrec_fid); snprintf(indexfile,200,"%s/index_%03d",index_dirk,Nth); // /.../.fotoxx/image_index/index_NNN fid = fopen(indexfile,"r"); // open index_NNN file sxrec_fid = fid; if (! fid) goto file_err; *buff = 0; // no prior data in buffer } if (! strmatchN(buff,"file: ",6) || ! strmatch(buff+6,file)) // file in buffer not my file { while (true) // loop until my file found { pp = fgets_trim(buff,indexrecl,fid); // read existing records if (! pp) break; // EOF if (! strmatchN(pp,"file: ",6)) continue; // to start of next file set nn = image_fcomp(pp+6,file); if (nn >= 0) break; // same or after my file } if (! pp || nn > 0) { // EOF or after, file not found fclose(fid); sxrec_fid = 0; return 1; } } pNth = Nth; // set up for poss. continuation strcpy(pfile,file); sxrec.file = zstrdup(file); // build sxrec while (true) // get recs following "file" rec. { pp = fgets_trim(buff,indexrecl,fid); if (! pp) break; if (strmatchN(pp,"file: ",6)) break; // ran into next file rec. if (strmatchN(pp,"date: ",6)) { // EXIF (photo) date pp += 6; pp2 = strField(pp,' ',1); // EXIF (photo) date, yyyymmddhhmmss if (pp2) strncpy0(sxrec.pdate,pp2,15); pp2 = strField(pp,' ',2); if (pp2) strncpy0(sxrec.fdate,pp2,15); // file date, yyyymmddhhmmss } else if (strmatchN(pp,"stars: ",7)) { // rating, '0' to '5' stars sxrec.rating[0] = *(pp+7); sxrec.rating[1] = 0; } else if (strmatchN(pp,"size: ",6)) // size, "NNNNxNNNN" strncpy0(sxrec.size,pp+6,15); else if (strmatchN(pp,"tags: ",6)) // tags sxrec.tags = zstrdup(pp+6); else if (strmatchN(pp,"capt: ",6)) // caption sxrec.capt = zstrdup(pp+6); else if (strmatchN(pp,"comms: ",7)) // comments sxrec.comms = zstrdup(pp+7); else if (strmatchN(pp,"gtags: ",7)) // geotags sxrec.gtags = zstrdup(pp+7); } if (! sxrec.pdate[0]) // supply defaults for missing items strcpy(sxrec.pdate,"null"); if (! sxrec.rating[0]) strcpy(sxrec.rating,"0"); if (! sxrec.size[0]) strcpy(sxrec.size,"null"); if (! sxrec.tags) sxrec.tags = zstrdup("null, "); if (! sxrec.capt) sxrec.capt = zstrdup("null"); if (! sxrec.comms) sxrec.comms = zstrdup("null"); if (! sxrec.gtags) sxrec.gtags = zstrdup("null^ null^ null^ null"); return 0; file_err: if (! logerr) printz("image index read error: %s \n",strerror(errno)); logerr++; if (fid) fclose(fid); sxrec_fid = 0; return 3; } /**************************************************************************/ // minimized version of get_sxrec() 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, 2+ if error (diagnosed) int get_sxrec_min(cchar *file, char *fdate, char *pdate, char *size) { FILE *fid = 0; char indexfile[200]; static char buff[indexrecl]; static char pfile[XFCC]; int Nth, nn = 0, Fcontinue; cchar *pp; static int pNth, logerr = 0; strcpy(fdate,""); // outputs = missing strcpy(pdate,"undated"); strcpy(size,""); Fcontinue = 1; // test if prior search can continue if (! sxrec_fid) Fcontinue = 0; // no prior search still open Nth = find_image_index_file(file); // get index file for this image file if (Nth != pNth) Fcontinue = 0; // index file not the same if (image_fcomp(file,pfile) <= 0) Fcontinue = 0; // req. file not after prior file if (Fcontinue) fid = sxrec_fid; else { if (sxrec_fid) fclose(sxrec_fid); snprintf(indexfile,200,"%s/index_%03d",index_dirk,Nth); // /.../.fotoxx/image_index/index_NNN fid = fopen(indexfile,"r"); // open index_NNN file if (! fid) goto file_err; sxrec_fid = fid; *buff = 0; // no prior data in buffer } if (! strmatchN(buff,"file: ",6) || ! strmatch(buff+6,file)) // file in buffer not my file { while (true) // loop until my file found { pp = fgets_trim(buff,indexrecl,fid); // read existing records if (! pp) break; // EOF if (! strmatchN(pp,"file: ",6)) continue; // to start of next file set nn = image_fcomp(pp+6,file); if (nn >= 0) break; // same or after my file } if (! pp || nn > 0) { // EOF or after, file not found fclose(fid); sxrec_fid = 0; return 1; } } pNth = Nth; // set up for poss. continuation strcpy(pfile,file); while (true) // get recs following "file" rec. { pp = fgets_trim(buff,indexrecl,fid); if (! pp) break; if (strmatchN(pp,"file: ",6)) break; // ran into next file rec. if (strmatchN(pp,"date: ",6)) // date record { pp = strField(buff,' ',2); if (pp) { if (*pp != 'n') strncpy0(pdate,pp,15); // photo date unless "null" pp = strField(buff,' ',3); if (pp) strncpy0(fdate,pp,15); // file date } } else if (strmatchN(pp,"size: ",6)) // size, "NNNNxNNNN" strncpy0(size,pp+6,15); } return 0; file_err: if (! logerr) printz("image index read error: %s \n",strerror(errno)); logerr++; if (fid) fclose(fid); sxrec_fid = 0; return 3; } /**************************************************************************/ // Add or update image index record for given image file. // If sxrec is null, delete index record. // Return 0 if success, +N if error. int put_sxrec(sxrec_t *sxrec, cchar *file) { FILE *fid1 = 0, *fid2 = 0; char indexfile[200], tempfile[200]; char buff[indexrecl]; char *pp; int err, nn, Nth; int filecount, Fcopy, Finsert, Finserted, Fnewfirst; STATB statb, statb2; if (sxrec_fid) { // stop get_sxrec() fclose(sxrec_fid); sxrec_fid = 0; } err = stat(file,&statb); // check image file exists if (err && sxrec) return 1; Nth = find_image_index_file(file); // construct index file snprintf(indexfile,200,"%s/index_%03d",index_dirk,Nth); // /.../.fotoxx/image_index/index_NNN err = stat(indexfile,&statb2); // missing? (don't clobber statb) 15.08 if (err) { nn = creat(indexfile,0640); // create if needed if (nn < 0) goto file_err; close(nn); } fid1 = fopen(indexfile,"r"); // open index_NNN file if (! fid1) goto file_err; strcpy(tempfile,indexfile); // temp image index file strcat(tempfile,"_temp"); fid2 = fopen(tempfile,"w"); // open for output if (! fid2) goto file_err; Finsert = Finserted = Fcopy = Fnewfirst = 0; filecount = 0; while (true) // copy input to output { pp = fgets_trim(buff,indexrecl,fid1); // read existing records if (pp && strmatchN(pp,"file: ",6)) // start of a file record set { Finsert = Fcopy = Fnewfirst = 0; nn = strcasecmp(file,pp+6); // compare input file to index file if (nn == 0) nn = strcmp(file,pp+6); if (nn <= 0 && sxrec && ! Finserted) Finsert = 1; // input <= index file, insert sxrec here if (nn != 0) Fcopy = 1; // input != index file, copy to output if (filecount == 0) { if (Finsert && nn < 0) Fnewfirst = 1; // detect if first image file in index if (! Fcopy) Fnewfirst = 1; // file will be changed or deleted } filecount += Fcopy + Finsert; } if (! pp && sxrec && ! Finserted) { // input EOF, insert at the end Finsert = 1; if (filecount == 0) Fnewfirst = 1; } if (! pp && (Finserted || ! sxrec)) break; // done if (Finsert && ! Finserted) { Finserted = 1; // new index file recs. go here nn = fprintf(fid2,"file: %s\n",file); // write new index file recs. if (! nn) goto file_err; compact_time(statb.st_mtime,sxrec->fdate); // convert to "yyyymmddhhmmss" if (! sxrec->pdate[0]) strcpy(sxrec->pdate,"null"); // EXIF (photo) date, yyyy:mm:dd nn = fprintf(fid2,"date: %s %s\n",sxrec->pdate,sxrec->fdate); // photo and file date rec. if (! nn) goto file_err; if (sxrec->rating[0]) nn = fprintf(fid2,"stars: %s\n",sxrec->rating); // rating rec. else nn = fprintf(fid2,"stars: 0\n"); if (! nn) goto file_err; if (sxrec->size[0]) nn = fprintf(fid2,"size: %s\n",sxrec->size); // size rec. else nn = fprintf(fid2,"size: null\n"); if (sxrec->tags) nn = fprintf(fid2,"tags: %s\n",sxrec->tags); // tags rec. else nn = fprintf(fid2,"tags: null, ""\n"); if (! nn) goto file_err; if (sxrec->capt) nn = fprintf(fid2,"capt: %s\n",sxrec->capt); // caption rec. else nn = fprintf(fid2,"capt: null\n"); if (! nn) goto file_err; if (sxrec->comms) nn = fprintf(fid2,"comms: %s\n",sxrec->comms); // comments rec. else nn = fprintf(fid2,"comms: null\n"); if (! nn) goto file_err; if (sxrec->gtags) nn = fprintf(fid2,"gtags: %s\n",sxrec->gtags); // geotags rec. else nn = fprintf(fid2,"gtags: null^ null^ null^ null\n"); if (! nn) goto file_err; nn = fprintf(fid2,"\n"); // EOL blank rec. if (! nn) goto file_err; } if (pp && Fcopy) { // copy input to output nn = fprintf(fid2,"%s\n",pp); // unless replaced by sxrec if (! nn) goto file_err; } } err = fclose(fid1); // close input file err = fclose(fid2); // close output file fid1 = fid2 = 0; if (err) goto file_err; err = rename(tempfile,indexfile); // replace index file with temp file if (err) goto file_err; if (filecount > image_index_max) // if index file too big, split split_image_index_file(Nth); else if (Fnewfirst) init_image_index(1); // update image index map return 0; file_err: zmessLogACK(Mwin,"image index write error 3\n %s",strerror(errno)); if (fid1) fclose(fid1); if (fid2) fclose(fid2); return 3; } /**************************************************************************/ // Read image index files sequentially, return one index rec. per call. // Set ftf = 1 for first read, will be reset to 0. // Returns 0 if OK, 1 if EOF, 2 if error (diagnosed). // Returned sxrec_t data has allocated fields subject to zfree(). int read_sxrec_seq(sxrec_t &sxrec, int &ftf) { char indexfile[200]; static FILE *fid = 0; static char buff[indexrecl]; static int Nth; cchar *pp, *pp2; int err; STATB statb; if (ftf) // initial call { ftf = 0; snprintf(indexfile,200,"%s/index_001",index_dirk); // first index file fid = fopen(indexfile,"r"); if (! fid) return 2; // no index file ? Nth = 1; *buff = 0; } while (true) { if (! strmatchN(buff,"file: ",6)) // next file rec. may be already there { while (true) // get start of next record set { pp = fgets_trim(buff,indexrecl,fid); if (pp && strmatchN(buff,"file: ",6)) break; if (pp) continue; fclose(fid); // EOF, start next index file Nth++; snprintf(indexfile,200,"%s/index_%03d",index_dirk,Nth); fid = fopen(indexfile,"r"); if (! fid) return 1; // no more, final EOF } } *buff = 0; // no longer match "file: " err = stat(buff+6,&statb); // check image file exists if (err) continue; if (S_ISREG(statb.st_mode)) break; } memset(&sxrec,0,sizeof(sxrec)); // clear record sxrec.file = zstrdup(buff+6); // image file name while (true) // get recs following "file" rec. { pp = fgets_trim(buff,indexrecl,fid); if (! pp) break; if (strmatchN(pp,"file: ",6)) break; // ran into next file rec. else if (strmatchN(pp,"date: ",6)) { pp += 6; pp2 = strField(pp,' ',1); // EXIF (photo) date, yyyymmddhhmmss if (pp2) strncpy0(sxrec.pdate,pp2,15); pp2 = strField(pp,' ',2); if (pp2) strncpy0(sxrec.fdate,pp2,15); // file mod date, yyyymmddhhmmss } else if (strmatchN(pp,"stars: ",7)) { // rating, '0' to '5' stars sxrec.rating[0] = *(pp+7); sxrec.rating[1] = 0; } else if (strmatchN(pp,"size: ",6)) // size, "NNNNxNNNN" strncpy0(sxrec.size,pp+6,15); else if (strmatchN(pp,"tags: ",6)) // tags sxrec.tags = zstrdup(pp+6); else if (strmatchN(pp,"capt: ",6)) // caption sxrec.capt = zstrdup(pp+6); else if (strmatchN(pp,"comms: ",7)) // comments sxrec.comms = zstrdup(pp+7); else if (strmatchN(pp,"gtags: ",7)) // geotags sxrec.gtags = zstrdup(pp+7); } if (! sxrec.pdate[0]) // supply defaults for missing items strcpy(sxrec.pdate,"null"); if (! sxrec.rating[0]) strcpy(sxrec.rating,"0"); if (! sxrec.size[0]) strcpy(sxrec.size,"null"); if (! sxrec.tags) sxrec.tags = zstrdup("null, "); if (! sxrec.capt) sxrec.capt = zstrdup("null"); if (! sxrec.comms) sxrec.comms = zstrdup("null"); if (! sxrec.gtags) sxrec.gtags = zstrdup("null^ null^ null^ null"); return 0; } /**************************************************************************/ // Write the image index files sequentially, 1 rec. per call // Set ftf = 1 for first call, will be reset to 0. // Set sxrec = 0 to close file after last write. // Returns 0 if OK, otherwise +N. // Used by index image files function. int write_sxrec_seq(sxrec_t *sxrec, int &ftf) { static int Nth, filecount; static FILE *fid = 0; char indexfile[200], oldirk[200]; int nn, err; STATB statb; if (sxrec_fid) { // stop get_sxrec() fclose(sxrec_fid); sxrec_fid = 0; } if (ftf) // first call { ftf = 0; err = stat(index_dirk,&statb); if (! err && S_ISREG(statb.st_mode)) { // rename old image_index strcpy(oldirk,index_dirk); strcat(oldirk,"_old"); rename(index_dirk,oldirk); } err = stat(index_dirk,&statb); // create new image_index directory if (err) err = mkdir(index_dirk,0750); // if not already there if (err) goto file_err; err = shell_quiet("rm -f -v %s/index_* > /dev/null",index_dirk); // delete all image index files Nth = 1; snprintf(indexfile,200,"%s/index_%03d",index_dirk,Nth); // create initial index file fid = fopen(indexfile,"w"); if (! fid) goto file_err; filecount = 0; } if (! sxrec) { // EOF call if (fid) { err = fclose(fid); fid = 0; if (err) goto file_err; } err = init_image_index(1); // initz. image index map return err; } err = stat(sxrec->file,&statb); // check image file exists if (err || ! S_ISREG(statb.st_mode)) { printz("file %s not found \n",sxrec->file); return 0; } compact_time(statb.st_mtime,sxrec->fdate); // convert to "yyyymmddhhmmss" if (filecount > 0.8 * image_index_max) { // if 80% max reached, err = fclose(fid); // start new index file fid = 0; if (err) goto file_err; if (++Nth > 999) { zmessLogACK(Mwin,"too many image index files"); return 2; } snprintf(indexfile,200,"%s/index_%03d",index_dirk,Nth); // open/write next index file fid = fopen(indexfile,"w"); if (! fid) goto file_err; filecount = 0; // is empty } filecount++; // new image file record set nn = fprintf(fid,"file: %s\n",sxrec->file); // output: filename rec. if (! nn) goto file_err; if (! sxrec->pdate[0]) strcpy(sxrec->pdate,"null"); // EXIF (photo) date, yyyymmddhhmmss nn = fprintf(fid,"date: %s %s\n",sxrec->pdate,sxrec->fdate); // photo and file date rec. if (! nn) goto file_err; if (sxrec->rating[0]) nn = fprintf(fid,"stars: %c\n",sxrec->rating[0]); // rating rec. else nn = fprintf(fid,"stars: 0\n"); if (! nn) goto file_err; if (sxrec->size[0]) nn = fprintf(fid,"size: %s\n",sxrec->size); else nn = fprintf(fid,"size: null\n"); if (sxrec->tags) nn = fprintf(fid,"tags: %s\n",sxrec->tags); // tags rec. else nn = fprintf(fid,"tags: null, ""\n"); if (! nn) goto file_err; if (sxrec->capt) nn = fprintf(fid,"capt: %s\n",sxrec->capt); // caption rec. else nn = fprintf(fid,"capt: null\n"); if (! nn) goto file_err; if (sxrec->comms) nn = fprintf(fid,"comms: %s\n",sxrec->comms); // comments rec. else nn = fprintf(fid,"comms: null\n"); if (! nn) goto file_err; if (sxrec->gtags) nn = fprintf(fid,"gtags: %s\n",sxrec->gtags); // geotags rec. else nn = fprintf(fid,"gtags: null^ null^ null^ null\n"); if (! nn) goto file_err; nn = fprintf(fid,"\n"); // EOL blank rec. if (! nn) goto file_err; return 0; file_err: zmessLogACK(Mwin,"image index write error 4\n %s",strerror(errno)); if (fid) fclose(fid); fid = 0; return 3; } // file name compare in image index sequence // use case blind compare first, then normal compare as a tiebreaker int image_fcomp(cchar *file1, cchar *file2) { int nn; nn = strcasecmp(file1,file2); // compare case blind if (nn != 0) return nn; nn = strcmp(file1,file2); // if equal, use utf8 compare return nn; } fotoxx-15.11.1/f.gallery.cc0000664000175000017500000035732112616075370014065 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** Fotoxx image edit - directory navigation and thumbnail gallery functions gallery create/update/search/paint an image gallery set_gwin_title update the gallery window title bar gallery_getnext get next file from current gallery position gallery_getlastver get the last version of a file in a gallery gallery_position get a file position within a gallery 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 image_thumbfile retrieve or create the thumbnail file for given image file image_thumbnail get thumbnail pixbuf for an image file from cache or disk get_thumbnail_pixbuf make thumbnail pixbuf from an image file popimage popup a larger image from a clicked thumbnail gallery_monitor monitor directory for file changes and update gallery gallery_getfiles gallery file selection function and dialog m_edit_bookmarks edit bookmarks m_goto_bookmark jump to a gallery/bookmark position ***************************************************************************/ #define EX extern // disable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /**************************************************************************/ namespace navi { #define maxgallerylevs 60 // max gallery navigation levels #define thumbnail_cachesize 10000 // max thumbnails cached in memory #define TEXTWIN GTK_TEXT_WINDOW_TEXT // GDK window of GTK text view #define NEVER GTK_POLICY_NEVER #define ALWAYS GTK_POLICY_ALWAYS #define thumbxx 6 // thumbx array size int thumbx[6] = { 512, 360, 256, 180, 128, 90 }; // thumbnail sizes typedef struct { // current gallery list in memory char *file; // /directory.../filename char fdate[16]; // file date: yyyymmddhhmmss char pdate[16]; // photo date: yyyy:mm:dd char size[16]; // image size: 2345x1234 } glist_t; glist_t *glist; // gallery type: 1-6 = directory, search results, search metadata results, album, recent images, newest images int gallerytype = 0; // 1/2/3/4/5/6 = see above char *galleryname = 0; // directory or file list name GtkWidget *gallerybutt[60]; // gallery navi buttons [aaa] [bbb] ... char *gallerypath[60]; // corresp. directory names aaa bbb ... int gallerysort = 1; // 1/2/3 = filename/filedate/photodate int galleryseq = 1; // 1/2 = ascending/descending int gallerypainted = 0; // gallery is finished painting int nfiles = 0; // gallery file count (incl. subdirks) int nimages = 0; // gallery image file count char **mdlist = 0; // corresp. metadata list int mdrows = 0; // text rows in metadata list int xwinW = 1000, xwinH = 700; // gallery window initial size int xwinX, xwinY; // gallery window initial position int thumbsize = 128; // initial thumbnail image size int thumbW, thumbH; // gallery window thumbnail cell size int fontsize = 9; // font size for text in gallery window 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 scrollp = 0; // scroll position int maxscroll; // max. scroll position int topfileposn = 0; // scroll-to file position (Nth) int gallery_scrollgoal = -1; // gallery scroll goal position int gallery_scrollspeed = 0; // gallery scroll speed pixels/second double thumbcache_MB = 0; // total thumbnail cache size in MB double thumbcache_max = 1000; // cache limit: 1 GB 15.08 // private functions char * navigate(cchar *filez, cchar *action, int Nth = 0); // image file list setup and navigate 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 void gallery_navibutts(); // create navigation buttons in top panel void dir_filecount(char *dirname, int &ndir, int &nfil); // get directory 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 menufuncx(GtkWidget *win, 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 on navigation button void newtop(GtkWidget *widget, GdkEventButton *event); // function for top image directory change int newtop_dialog_event(zdialog *zd, cchar *event); // "" dialog event function void newalbum(GtkWidget *widget, GdkEventButton *event); // function for top image directory change void getalbum(); // function to get gallery from an album int getalbum_dialog_event(zdialog *zd, cchar *event); // "" dialog event function void gallery_sort(); // choose gallery sort order int gallery_sort_dialog_event(zdialog *zd, cchar *event); // "" dialog event function void mouse_event(GtkWidget *widget, GdkEvent *event, void *); // gallery window mouse event function int KBrelease(GtkWidget *, GdkEventKey *, void *); // gallery window key release event } using namespace zfuncs; using namespace navi; /************************************************************************** public function to create/update image gallery (thumbnail window) Make a scrolling window of thumbnails for a list of files Handle window buttons (up row, down page, open file, etc.) Call external functions in response to thumbnail mouse-clicks. char * gallery(cchar *filez, cchar *action, int Nth) filez: image file or directory, or file with list of image files action: init: filez = initial file or directory initF: filez = file with list of image files to use sort: sort the file list, directories first, ignore case insert: insert filez into file list at position Nth (0 to last+1) delete: delete Nth file in list find: return Nth file (0 base) or null if Nth > last get1st: return 1st image file or null if none paint: paint gallery window, filez in top row, Nth position if match (if filez null and Nth < 0, no change in rop row) Nth: file position in gallery (action = insert/delete/find/paint) (0 base) (Nth is optional argument, default = 0) thumbnail click functions: gallery_Lclick_func() default function (open file) gallery_Rclick_popup() default function (popup menu) gallery_getfiles_Lclick_func() gallery_getfiles active gallery_getfiles_Rclick_func() gallery_getfiles active bookmarks_Lclick_func edit bookmarks active passes Nth of clicked thumbnail (0 to last) passes -1 if gallery window is closed passes -2 if key F1 is pressed (for context help) Returned values: find: filespec, others: null The returned file belongs to caller and is subject for zfree(). ***************************************************************************/ char * gallery(cchar *filez, cchar *action, int Nth) // overhauled { int nth = Nth; zthreadcrash(); // thread usage not allowed if (strstr("init initF",action)) { // generate new gallery file list gallerypainted = 0; // from directory/file navigate(filez,action); return 0; } if (strstr("sort insert delete",action)) { // revise gallery file list navigate(filez,action,Nth); gallerypainted = 0; return 0; } if (strmatch(action,"find")) // find Nth file in file list return navigate(0,action,Nth); if (strmatch(action,"get1st")) // get 1st image file in file list return navigate(0,action); if (strmatch(action,"paint")) { // paint gallery window gallerypainted = 0; if (filez) nth = gallery_position(filez,Nth); // use Nth if valid, else find Nth topfileposn = nth; // filez or -1 or caller Nth gtk_widget_queue_draw(Gdrawin); // draw gallery window return 0; } zappcrash("gallery() action: %s",action); // bad call return 0; } /************************************************************************** private function - manage list of image files within a directory char * navi::navigate(cchar *filez, cchar *action, int Nth) action: init: file list = directories and image files in directory filez initF: file list = filez = list of image files to use (cchar **) sort: sort the file list, directories first, ignore case insert: insert filez into file list at position Nth (0 to last+1) delete: delete Nth file in list find: returns Nth file or null if Nth > last get1st: return 1st image file or null if none Nth: file to find/insert/delete (0 base) (used for initF galleries: album or search results) Returned values: find: filespec, else null The returned file belongs to caller and is subject for zfree(). ***************************************************************************/ char * navi::navigate(cchar *filez, cchar *action, int Nth) { char *file, *findname; cchar *findcommand = "find -L \"%s\" -maxdepth 1"; char *pp, *file2, fdate[16], pdate[16], size[16]; int err, ii, cc, ftyp, contx = 0, fposn; FILE *fid; STATB statbuf; if (! strstr("init initF sort insert delete find get1st",action)) zappcrash("navigate %s",action); if (strmatchN(action,"init",4)) // init or initF { if (! filez && gallerytype == 1) filez = galleryname; // refresh gallery 15.03 if (! filez) return 0; if (glist) { for (ii = 0; ii < nfiles; ii++) // free prior gallery list zfree(glist[ii].file); zfree(glist); glist = 0; } if (mdlist) { // clear prior metadata if any for (ii = 0; ii < nfiles; ii++) if (mdlist[ii]) zfree(mdlist[ii]); zfree(mdlist); mdlist = 0; } cc = maximages * sizeof(glist_t); glist = (glist_t *) zmalloc(cc); // gallery file list nfiles = nimages = 0; // no files fposn = 0; if (galleryname && filez != galleryname) // don't destroy input zfree(galleryname); galleryname = zstrdup(filez); } if (strmatch(action,"init")) // initialize from given directory { gallerytype = 1; // gallery type = directory err = stat(galleryname,&statbuf); if (err) { pp = (char *) strrchr(galleryname,'/'); // bad file, check directory part if (! pp) return 0; pp[1] = 0; 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[1] = 0; } if (strchr(galleryname,'"')) { // if galleryname has embedded quotes, cc = strlen(galleryname); // they must be escaped for "find" findname = (char *) zmalloc(cc+40); repl_1str(galleryname,findname,"\"","\\\""); } else findname = zstrdup(galleryname); while ((file = command_output(contx,findcommand,findname))) // find all files { if (strmatch(file,galleryname)) { // skip self directory zfree(file); continue; } if (nfiles == maximages) { zmessageACK(Mwin,Btoomanyfiles,maximages); break; } ftyp = image_file_type(file); if (ftyp == 1) { // subdirectory if (! Fshowhidden) { pp = strrchr(file,'/'); // suppress hidden directories 15.04 if (pp && pp[1] == '.') continue; } glist[nfiles].file = file; // add to file list glist[nfiles].file[0] = '!'; // if directory, make it sort first glist[nfiles].fdate[0] = 0; // no file date glist[nfiles].pdate[0] = 0; // no photo date nfiles++; } else if (ftyp == 2 || ftyp == 3) { // supported image or RAW file err = stat(file,&statbuf); if (err) continue; glist[nfiles].file = file; // add to file list err = get_sxrec_min(file,fdate,pdate,size); // file date, photo date, size strcpy(glist[nfiles].fdate,fdate); strcpy(glist[nfiles].pdate,pdate); nfiles++; nimages++; } else { zfree(file); // (thumbnails not ftyp 1) continue; } } zfree(findname); if (nfiles > 1) // sort the glist records HeapSort((char *) glist, sizeof(glist_t), nfiles, gallery_comp); gallery_monitor("start"); // monitor gallery file changes curr_file_count = nimages; // gallery image file count return 0; } if (strmatch(action,"initF")) // initialize from given file list { if (gallerytype < 2) zappcrash("gallerytype %d",gallerytype); // gallery type from caller fid = fopen(galleryname,"r"); // open file if (! fid) return 0; file = (char *) zmalloc(XFCC); while (true) // read list of files { pp = fgets_trim(file,XFCC-1,fid,1); if (! pp) break; err = stat(pp,&statbuf); // check file exists if (err) continue; glist[nfiles].file = zstrdup(pp); // add to file list get_sxrec_min(pp,fdate,pdate,size); // file date, photo date, size strcpy(glist[nfiles].fdate,fdate); strcpy(glist[nfiles].pdate,pdate); nfiles++; if (nfiles == maximages) { zmessageACK(Mwin,Btoomanyfiles,maximages); break; } } fclose(fid); zfree(file); nimages = nfiles; curr_file_count = nimages; // gallery image file count if (curr_file) { Nth = gallery_position(curr_file,0); // current file in the new gallery? curr_file_posn = Nth; // set curr. file posn. or -1 if not } return 0; } if (strmatch(action,"sort")) // sort the file list from init { if (nfiles < 2) return 0; HeapSort((char *) glist, sizeof(glist_t), nfiles, gallery_comp); // sort the glist records return 0; } if (strmatch(action,"insert")) // insert new file into list { if (gallerytype == 3) return 0; // metadata report 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 glist[ii] = glist[ii-1]; glist[fposn].file = zstrdup(filez); // put new file in hole get_sxrec_min(filez,fdate,pdate,size); // file date, photo date, size strcpy(glist[nfiles].fdate,fdate); strcpy(glist[nfiles].pdate,pdate); nfiles++; nimages++; // bugfix } 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 (*glist[fposn].file != '!') nimages--; // not a directory, reduce image count zfree(glist[fposn].file); // remove glist record for (ii = fposn; ii < nfiles; ii++) { // close the hole if (nfiles < 0) printz("meaningless reference %d",ii); // stop g++ optimization bug //// glist[ii] = glist[ii+1]; } if (mdlist) { if (mdlist[fposn]) zfree(mdlist[fposn]); // delete corresp. metadata for (ii = fposn; ii < nfiles; ii++) { // close the hole if (nfiles < 0) printz("meaningless reference %d",ii); // stop g++ optimization bug //// mdlist[ii] = mdlist[ii+1]; } } } if (strmatch(action,"find")) // return Nth file in gallery { fposn = Nth; // file position from caller must be OK if (fposn < 0 || fposn > nfiles-1) return 0; file2 = zstrdup(glist[fposn].file); // get Nth file file2[0] = '/'; // restore initial '/' err = stat(file2,&statbuf); if (! err) return file2; zfree(file2); } if (strmatch(action,"get1st")) // return 1st image file in gallery { for (Nth = 0; Nth < nfiles; Nth++) { if (glist[Nth].file[0] == '!') continue; // subdirectory file2 = zstrdup(glist[Nth].file); // get Nth file err = stat(file2,&statbuf); if (! err) return file2; zfree(file2); } return 0; } return 0; } // private function for special file name compare // directories sort first and upper/lower case is ignored int navi::gallery_comp(cchar *rec1, cchar *rec2) { int nn; glist_t *grec1, *grec2; if (galleryseq == 1) { // ascending grec1 = (glist_t *) rec1; grec2 = (glist_t *) rec2; } else { // descending grec1 = (glist_t *) rec2; grec2 = (glist_t *) rec1; } switch (gallerysort) { case 1: { // file name nn = strcasecmp(grec1->file,grec2->file); if (nn != 0) return nn; nn = strcmp(grec1->file,grec2->file); // if equal, use utf8 compare return nn; } case 2: { // file mod date/time nn = strcmp(grec1->fdate,grec2->fdate); return nn; } case 3: { // photo date/time nn = strcmp(grec1->pdate,grec2->pdate); // (EXIF DateTimeOriginal) return nn; } default: return 0; } } // private function // paint gallery window - draw all thumbnail images that can fit int navi::gallery_paint(GtkWidget *drwin, cairo_t *cr) { PIXBUF *pxbT; double x1, y1, x2, y2; int ii, nrows, row, col; int row1, row2, ftyp, ww, hh; int drwingW, drwingH; int thumx, thumy; int ndir, nfil; char *pp, *fname, p0; char text[200], fdate[16], pdate[20], size[16]; gallerypainted = 0; if (! galleryname) { if (! curr_dirk) return 1; gallery(curr_dirk,"init"); } set_gwin_title(); // main window title = gallery name gallery_navibutts(); // add navigation buttons to top panel if (gallerytype == 3) { // metadata report gallery_paintmeta(drwin,cr); return 1; } fontsize = 7 + thumbsize / 128; // font size from 7 to 10 if (! thumbsize) fontsize = 9; // text only view texthh = 3.5 * fontsize + 4; // two text lines thumbW = thumbsize + 10; // thumbnail cell size thumbH = thumbsize + texthh + thumbsize/24 + 10; if (! thumbsize) { thumbW = 400; // zero, list view thumbH = 40; } 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; // last row if (drwingH < xwinH) drwingH = xwinH; // at least current size 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_step_increment(Gadjust,thumbH); // scrollbar works in row steps gtk_adjustment_set_page_increment(Gadjust,thumbH * xrows); // and in page steps if (topfileposn >= 0) { // new target file position (Nth) scrollp = topfileposn / xcols * thumbH; // scroll position for target file if (scrollp > maxscroll) scrollp = maxscroll; // in top row of window gtk_adjustment_set_upper(Gadjust,maxscroll); gtk_adjustment_set_value(Gadjust,scrollp); // will cause re-entrance gtk_widget_queue_draw(drwin); topfileposn = -1; // keep scroll position next time return 1; } else { 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 p0 = *glist[ii].file; // replace possible '!' with '/' *glist[ii].file = '/'; fname = glist[ii].file; // filespec pp = strrchr(fname,'/'); // get file name only if (pp) fname = pp + 1; else fname = (char *) "?"; thumx = col * thumbW + margin; // upper left corner in drawing area thumy = row * thumbH + margin; if (curr_file && strmatch(glist[ii].file,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); } ftyp = image_file_type(glist[ii].file); // 1/2/3 = directory/image/RAW file if (ftyp == 1) { // directory dir_filecount(glist[ii].file,ndir,nfil); // get subdir and file counts 15.11 snprintf(text,200,"%s\n%d + %d images",fname,ndir,nfil); // dir name, subdirs + image files } if (ftyp == 2 || ftyp == 3) { // image or RAW file get_sxrec_min(glist[ii].file,fdate,pdate,size); // filename, file/photo date, size (WxH) if (gallerysort == 2) pp = fdate; else pp = pdate; // use file or photo date based on sort if (! strmatch(pp,"undated")) { memcpy(pp+14,pp+10,2); // convert yyyymmddhhmm to yyyy-mm-dd hh:mm memcpy(pp+11,pp+8,2); memcpy(pp+8,pp+6,2); memcpy(pp+5,pp+4,2); pp[16] = 0; pp[13] = ':'; pp[10] =' '; pp[4] = pp[7] = '-'; } snprintf(text,200,"%s\n%-16s %s",fname,pp,size); // text is name, date, size } if (thumbsize) // thumbnails view { if (ftyp == 2 || ftyp == 3) { // image or RAW file draw_text(cr,text,thumx,thumy,thumbW-5); // paint text first thumy += texthh; // position thumbnail below text } pxbT = image_thumbnail(glist[ii].file,thumbsize); // get thumbnail if (pxbT) { ww = gdk_pixbuf_get_width(pxbT); ww = (thumbsize - ww) / 4; // shift margin if smaller width 15.01 gdk_cairo_set_source_pixbuf(cr,pxbT,thumx+ww,thumy); cairo_paint(cr); // paint g_object_unref(pxbT); } if (ftyp == 1) { // directory 15.02 thumy += thumbsize/3 + 10; // overlay thumbnail with text fontsize++; draw_text(cr,text,thumx+ww+thumbW/6,thumy,thumbW-5); fontsize--; } } if (! thumbsize) // list view, no thumbnails draw_text(cr,text,thumx,thumy,thumbW-5); // paint text *glist[ii].file = p0; // restore '!' } endloops: 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, nrows, row, col; int row1, row2, ww, hh; int drwingW, drwingH; int thumx, thumy, textww; char p0; if (thumbsize < 128) thumbsize = 128; fontsize = 7 + thumbsize / 128; // font size from 7 to 10 thumbW = thumbsize + 10; // thumbnail layout size thumbH = thumbsize + 20; texthh = mdrows * 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 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_step_increment(Gadjust,thumbH); // scrollbar works in row steps gtk_adjustment_set_page_increment(Gadjust,thumbH * xrows); // and in page steps if (topfileposn >= 0) { // new target file position (Nth) scrollp = topfileposn / xcols * thumbH; // scroll position for target file if (scrollp > maxscroll) scrollp = maxscroll; // in top row of window gtk_adjustment_set_upper(Gadjust,maxscroll); gtk_adjustment_set_value(Gadjust,scrollp); // will cause re-entrance gtk_widget_queue_draw(drwin); topfileposn = -1; // keep scroll position next time return 1; } else { 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 { for (col = 0; col < xcols; col++) { ii = row * xcols + col; // next file if (ii >= nfiles) goto endloops; // exit 2 nested loops p0 = *glist[ii].file; // replace possible ! with / *glist[ii].file = '/'; thumx = col * thumbW + margin; // upper left corner in window space thumy = row * thumbH + margin; pxbT = image_thumbnail(glist[ii].file,thumbsize); // get thumbnail if (pxbT) { gdk_cairo_set_source_pixbuf(cr,pxbT,thumx,thumy); cairo_paint(cr); g_object_unref(pxbT); } draw_text(cr,glist[ii].file,thumbW+margin,thumy,textww); // write filespec to right of thumbnail if (mdlist && mdlist[ii]) // write metadata if present draw_text(cr,mdlist[ii],thumbW+margin,thumy+20,textww); *glist[ii].file = p0; // restore '!' } } endloops: gallerypainted = 1; return 1; } // private function // create a row of navigation buttons in gallery top panel void navi::gallery_navibutts() // 15.07 { int ii, cc, max = maxgallerylevs; char *pp1, *pp2; for (ii = 0; ii < max; ii++) { // clear old buttons if any if (gallerypath[ii]) { zfree(gallerypath[ii]); gallerypath[ii] = 0; gtk_widget_destroy(gallerybutt[ii]); } } if (gallerytype != 1) { 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 // find the number of subdirs and image files within a given directory void navi::dir_filecount(char *dirname, int &ndir, int &nfil) // 15.11 { char *file; cchar *findcommand = "find -L \"%s\" -maxdepth 1"; int ii, ftype, cc, err; int dcount = 0, fcount = 0, contx = 0; #define NC 1000 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); } 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; } } while ((file = command_output(contx,findcommand,dirname))) { // count included files and directories ftype = image_file_type(file); zfree(file); if (ftype == 1) dcount++; // 1/2/3 = directory/image/RAW file else if (ftype <= 3) fcount++; } dcount--; // remove self-count 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 - 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; char *file; if (! gallerypainted) return; // wait for pending paint if (! strmatch(menu,ZTX("Scroll"))) gallery_scroll(-1,0); // stop scrolling scrollp = gtk_adjustment_get_value(Gadjust); // current scroll position if (strstr(menu,ZTX("Sync"))) // curr. file directory --> gallery 15.02 { F1_help_topic = "sync_gallery"; // 15.07 if (! curr_file) return; m_viewmode(0,"G"); gallery(curr_file,"init"); gallery(curr_file,"paint"); return; } if (strmatch(menu,ZTX("Open"))) { // change directory file = zgetfile(ZTX("change directory"),MWIN,"folder",curr_dirk); if (! file) return; gallery(file,"init"); gallery(0,"paint",0); zfree(file); return; } if (strmatch(menu,ZTX("GoTo"))) { F1_help_topic = "bookmarks"; m_goto_bookmark(0,0); return; } if (strmatch(menu,ZTX("Sort"))) { // choose gallery sort order 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]; topfileposn = scrollp / thumbH * xcols; // keep top row position gtk_widget_queue_draw(Gdrawin); 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 < 128 && gallerytype == 3) // min. for metadata report thumbsize = 128; topfileposn = scrollp / thumbH * xcols; // keep top row position gtk_widget_queue_draw(Gdrawin); return; } if (strmatch(menu,ZTX("Row↑"))) { // scroll 1 row up scroll1 = scrollp / thumbH * thumbH; scroll2 = scroll1 - thumbH; if (scroll2 < 0) scroll2 = 0; gallery_scroll(scroll2,3000); return; } if (strmatch(menu,ZTX("Row↓"))) { // scroll 1 row down scroll1 = scrollp / thumbH * thumbH; scroll2 = scroll1 + thumbH; if (scroll2 > maxscroll) scroll2 = maxscroll; gallery_scroll(scroll2,3000); return; } if (strmatch(menu,ZTX("Page↑"))) { // scroll 1 page up scroll1 = scrollp / thumbH * thumbH; scroll2 = scroll1 - thumbH * xrows; if (scroll2 < 0) scroll2 = 0; gallery_scroll(scroll2,5000); return; } if (strmatch(menu,ZTX("Page↓"))) { // scroll 1 page down scroll1 = scrollp / thumbH * thumbH; scroll2 = scroll1 + thumbH * xrows; if (scroll2 > maxscroll) scroll2 = maxscroll; gallery_scroll(scroll2,5000); return; } if (strmatch(menu,ZTX("Scroll"))) { // start/stop slow scroll down if (gallery_scrollgoal >= 0) gallery_scroll(-1,0); else gallery_scroll(maxscroll,500); return; } if (strmatch(menu,ZTX("First"))) scrollp = 0; // jump to top or bottom if (strmatch(menu,ZTX("Last"))) scrollp = maxscroll; topfileposn = scrollp / thumbH * xcols; gtk_widget_queue_draw(Gdrawin); 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(2,gallery_scrollfunc,0); // 2 millisec. timer period return; } gallery_scrollgoal = position; // continue scrolling with gallery_scrollspeed = speed; // possible goal/speed change return; } // private function // timer function, runs every 2 milliseconds int navi::gallery_scrollfunc(void *) { static float cumscroll = 0; if (gallery_scrollgoal < 0) { // stop scrolling gallery_scrollspeed = 0; cumscroll = 0; return 0; } if (FGW != 'G') { // not gallery view gallery_scrollgoal = -1; // stop scrolling gallery_scrollspeed = 0; cumscroll = 0; return 0; } if (scrollp == gallery_scrollgoal) { // goal reached, stop gallery_scrollgoal = -1; gallery_scrollspeed = 0; cumscroll = 0; return 0; } cumscroll += 0.002 * gallery_scrollspeed; // based on 2 millisec. timer if (cumscroll < 1.0) return 1; // not yet one pixel if (scrollp < gallery_scrollgoal) { // adjust scroll position scrollp += cumscroll; if (scrollp > gallery_scrollgoal) scrollp = gallery_scrollgoal; } if (scrollp > gallery_scrollgoal) { scrollp -= cumscroll; if (scrollp < gallery_scrollgoal) scrollp = gallery_scrollgoal; } gtk_adjustment_set_value(Gadjust,scrollp); cumscroll = 0; return 1; } // private function // gallery navigation button clicked, open corresponding directory void navi::navibutt_clicked(GtkWidget *widget, int *lev) // 15.07 { char gallerydir[XFCC], *pp; pp = gallerydir; for (int ii = 0; ii <= *lev; ii++) { *pp = '/'; strcpy(pp+1,gallerypath[ii]); pp = pp + strlen(pp); } gallery(gallerydir,"init"); // initialize new gallery gallery(0,"paint",0); // paint new gallery return; } // private function - [TOP] button: select new top directory void navi::newtop(GtkWidget *widget, GdkEventButton *event) { zdialog *zd; char dirk[200], *pp; gallery_scroll(-1,0); // stop scrolling zd = zdialog_new(ZTX("Choose image directory"),Mwin,null); // popup dialog with top image directories zdialog_set_decorated(zd,0); // 15.07 zdialog_add_widget(zd,"combo","top","dialog"); for (int ii = 0; ii < Ntopdirks; ii++) // insert all top image directories zdialog_cb_app(zd,"top",topdirks[ii]); zdialog_cb_app(zd,"top","/"); // add "/" and "HOME" zdialog_cb_app(zd,"top","HOME"); zdialog_cb_app(zd,"top",ZTX("recent")); // add "recent" and "newest" zdialog_cb_app(zd,"top",ZTX("newest")); zdialog_resize(zd,200,0); zdialog_run(zd,newtop_dialog_event,"mouse"); // run dialog, wait for response zdialog_cb_popup(zd,"top"); zdialog_wait(zd); if (zd->zstat == 1) zdialog_fetch(zd,"top",dirk,200); // get user choice else *dirk = 0; zdialog_free(zd); if (! *dirk) return; zmainloop(); if (strmatch(dirk,ZTX("recent"))) { m_recentfiles(0,0); return; } if (strmatch(dirk,ZTX("newest"))) { m_newfiles(0,0); return; } if (strmatch(dirk,"HOME")) // if HOME, get /home/ directory if ((pp = getenv("HOME"))) if (pp) strncpy0(dirk,pp,200); gallery(dirk,"init"); gallery(0,"paint",0); // paint new gallery return; } int navi::newtop_dialog_event(zdialog *zd, cchar *event) // dialog event function { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"top")) zd->zstat = 1; // finish dialog when choice is made else zd->zstat = 2; return 1; } // private function - [Album] button: select new album void navi::newalbum(GtkWidget *widget, GdkEventButton *event) // 15.08 { gallery_scroll(-1,0); // stop scrolling getalbum(); return; } // private function // show a list of albums and choose a one which becomes current gallery // called by [Top] button menu in gallery view void navi::getalbum() { cchar *findcomm = "find -L \"%s\" -type f"; char *albums[100], albumname[100], albumfile[200]; char *file, *pp; int ii, contx = 0, count = 0; zdialog *zd; 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); zd = zdialog_new(ZTX("Choose album"),Mwin,null); // popup dialog with albums list zdialog_set_decorated(zd,0); // 15.07 zdialog_add_widget(zd,"combo","albums","dialog"); for (ii = 0; ii < count; ii++) // insert album file names zdialog_cb_app(zd,"albums",albums[ii]); zdialog_resize(zd,250,0); zdialog_run(zd,getalbum_dialog_event,"mouse"); // run dialog, wait for response zdialog_cb_popup(zd,"albums"); zdialog_wait(zd); if (zd->zstat != 1) { zdialog_free(zd); for (ii = 0; ii < count; ii++) zfree(albums[ii]); return; } zdialog_fetch(zd,"albums",albumname,100); // get user choice zdialog_free(zd); for (ii = 0; ii < count; ii++) zfree(albums[ii]); snprintf(albumfile,200,"%s/%s",albums_dirk,albumname); navi::gallerytype = 4; gallery(albumfile,"initF"); gallery(0,"paint",0); return; } int navi::getalbum_dialog_event(zdialog *zd, cchar *event) // dialog event function { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"albums")) zd->zstat = 1; // finish dialog when choice is made else zd->zstat = 2; return 1; } // private function // gallery sort by file name, file date, or photo date (exif) void navi::gallery_sort() { zdialog *zd; int zstat, nn; gallery_scroll(-1,0); // stop scrolling 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"); if (gallerysort == 1) zdialog_stuff(zd,"filename",1); // stuff current sort order if (gallerysort == 2) zdialog_stuff(zd,"filedate",1); if (gallerysort == 3) zdialog_stuff(zd,"photodate",1); if (galleryseq == 1) zdialog_stuff(zd,"ascending",1); // ascending/descending else zdialog_stuff(zd,"descending",1); zdialog_run(zd,gallery_sort_dialog_event,"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 = 1; zdialog_fetch(zd,"filedate",nn); if (nn) gallerysort = 2; zdialog_fetch(zd,"photodate",nn); if (nn) gallerysort = 3; zdialog_fetch(zd,"ascending",nn); if (nn) galleryseq = 1; else galleryseq = 2; zdialog_free(zd); gallery(0,"sort"); // sort the gallery gallery(0,"paint",0); return; } // dialog event and completion function int navi::gallery_sort_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 return 1; } // 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; GdkWindow *gdkwin; static int Bdown, Fdrag, bdNth, bdtime; int evtype, evtime, mousex, mousey, mousebutt; int row, col, nrows, tww, thh, marg; int Nth, poswidth, posheight, err; char *filez; STATB statb; if (! nfiles) return; // empty gallery if (! gallerypainted) return; // not initialized eventB = (GdkEventButton *) event; evtype = eventB->type; evtime = eventB->time; 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) { // bugfix 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; if (evtype == GDK_MOTION_NOTIFY) { if (! Bdown) return; if (Fdrag) goto scroll; // continue album drag if (bdNth < 0) return; if (evtime - bdtime < 500) return; album_drag_start(bdNth); // start album image drag Fdrag = 1; bdNth = -1; return; } if (evtype == GDK_BUTTON_PRESS) { gallery_scroll(-1,0); // stop scrolling if (Fdrag) { gdkwin = gtk_widget_get_window(widget); // 15.02 gdk_window_set_cursor(gdkwin,null); Fdrag = 0; } if (mousebutt == 1) { Bdown = 1; bdNth = Nth; // image at mouse when button pressed bdtime = evtime; } else { Bdown = 0; bdNth = -1; } return; } if (evtype == GDK_BUTTON_RELEASE) { gallery_scroll(-1,0); // stop scrolling Bdown = 0; if (Fdrag) { // drag done, drop image here if (poswidth > 50) Nth++; album_drag_drop(Nth); Fdrag = 0; bdNth = -1; return; } filez = zstrdup(glist[Nth].file); // file (thumbnail) at mouse position *filez = '/'; err = stat(filez,&statb); if (err) { // file is gone? zfree(filez); return; } if (S_ISDIR(statb.st_mode)) { // if directory, go there gallery(filez,"init"); gallery(0,"paint",0); // paint new gallery zfree(filez); return; } if (clicked_file) zfree(clicked_file); // save clicked file and gallery position clicked_file = filez; clicked_posn = Nth; clicked_width = poswidth; // normalized 0-100 clicked_height = posheight; if (thumbsize) { // 15.02 pxbT = image_thumbnail(filez,thumbsize); // get thumbnail image if (pxbT) { tww = gdk_pixbuf_get_width(pxbT); // thumbnail width and height thh = gdk_pixbuf_get_height(pxbT); g_object_unref(pxbT); marg = (thumbsize - tww) / 4; // allow for margin offset 15.02 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_getfiles) gallery_getfiles_Lclick_func(Nth); // send to gallery_getfiles() 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 gallery_Lclick_func(Nth); // open the file return; } if (mousebutt == 3) { // right click if (zd_gallery_getfiles) gallery_getfiles_Rclick_func(Nth); // send to gallery_getfiles() else gallery_Rclick_popup(Nth); // send to gallery thumbnail popup menu return; } } scroll: // scroll the gallery page int top, mpos, speed; top = gtk_adjustment_get_value(Gadjust); // mouse vertical posn in window mpos = 100 * (mousey - top) / xwinH; // 0 - 100 if (mpos < 20 && top > 0) { // mouse position from window top to bottom if (mpos < 0) mpos = 0; // 0 ...... 20 .......... 80 ...... 100 speed = 100 * (20 - mpos); // corresponding scroll speed gallery_scroll(0,speed); // 2000 .... 100 ... 0 ... 100 ...... 2000 } // down down up up if (mpos > 80 && top < maxscroll) { if (mpos >= 100) mpos = 100; speed = 100 * (mpos - 80); gallery_scroll(maxscroll,speed); } if (mpos >= 20 && mpos <= 80) // stop scrolling in mid-window range gallery_scroll(-1,0); return; } // Private function - respond to keyboard navigation keys. // KBrelease() for main window calls this function when G view is active. // key definitions: /usr/include/gtk-2.0/gdk/gdkkeysyms.h int navi::KBrelease(GtkWidget *win, GdkEventKey *event, void *) { int KBkey; KBkey = event->keyval; gallery_scroll(-1,0); // stop scrolling if (KBkey == GDK_KEY_plus) menufuncx(win,ZTX("Zoom+")); // +/- = bigger/smaller thumbnails if (KBkey == GDK_KEY_equal) menufuncx(win,ZTX("Zoom+")); if (KBkey == GDK_KEY_minus) menufuncx(win,ZTX("Zoom-")); if (KBkey == GDK_KEY_KP_Add) menufuncx(win,ZTX("Zoom+")); // keypad +/- also if (KBkey == GDK_KEY_KP_Subtract) menufuncx(win,ZTX("Zoom-")); if (KBkey == GDK_KEY_Left) menufuncx(win,ZTX("Page↑")); // left arrow = previous page if (KBkey == GDK_KEY_Right) menufuncx(win,ZTX("Page↓")); // right arrow = next page if (KBkey == GDK_KEY_Up) menufuncx(win,ZTX("Row↑")); // up arrow = previous row if (KBkey == GDK_KEY_Down) menufuncx(win,ZTX("Row↓")); // down arrow = next row if (KBkey == GDK_KEY_Home) menufuncx(win,ZTX("First")); // keys added if (KBkey == GDK_KEY_End) menufuncx(win,ZTX("Last")); if (KBkey == GDK_KEY_Page_Up) menufuncx(win,ZTX("Page↑")); if (KBkey == GDK_KEY_Page_Down) menufuncx(win,ZTX("Page↓")); return 1; } // set the window title for the gallery window // window title = gallery name void set_gwin_title() { char *pp, title[200]; int gtype; if (FGW != 'G') return; gtype = navi::gallerytype; if (gtype == 1) snprintf(title,200,"DIRECTORY %s %d files",galleryname,nfiles); else if (gtype == 2 || gtype == 3) snprintf(title,200,"SEARCH RESULTS %d files",nimages); else if (gtype == 4) { pp = strrchr(navi::galleryname,'/'); if (! pp) pp = navi::galleryname; else pp++; snprintf(title,200,"ALBUM %s %d files",pp,nimages); } else if (gtype == 5) strcpy(title,"RECENT FILES"); else if (gtype == 6) 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 version is returned. // (list must be sorted by file name (version sequence)). // Returns null if no previous/next file exists. // returned file is subject for zfree(). char * gallery_getnext(int index, int lastver) { int cc, Nth, loNth, hiNth, ftype; char *pp, *nfile = 0, *pfile = 0; if (! curr_file) { if (index < 0) Nth = last_file_posn - 1; // minor bugfix 15.05 else Nth = last_file_posn; if (index < 0) index = 0; if (index >= nfiles) Nth = nfiles - 1; nfile = gallery(0,"find",Nth); return nfile; } cc = strlen(curr_file); // find .ext and possible .vNN.ext pp = strrchr(curr_file,'.'); // /dirs ... /filename.vNN.ext if (! pp) return 0; cc = pp+1 - curr_file; if (strmatchN(pp-4,".v",2)) cc -= 4; // /dirs ... /filename. loNth = nfiles - nimages; // 1st image file (after directories) hiNth = nfiles - 1; // last Nth = curr_file_posn; // get prev/next file from here for (Nth += index; ; Nth += index) { if (nfile) zfree(nfile); if (Nth < loNth || Nth > hiNth) return pfile; // reached the end nfile = gallery(0,"find",Nth); // next file in gallery if (! nfile) return pfile; ftype = image_file_type(nfile); if (ftype != 2 && ftype != 3) continue; // not an image or RAW file if (! lastver) return nfile; // return every file version if (index < 0) { // direction = previous if (! strmatchN(nfile,curr_file,cc)) return nfile; // last file in previous filename group } else if (index > 0) { // direction = next if (pfile && ! strmatchN(nfile,pfile,cc)) { // last file in next filename group zfree(nfile); return pfile; } if (! pfile) { cc = strlen(nfile); // find .ext and possible .vNN.ext pp = strrchr(nfile,'.'); // /dirs ... /filename.vNN.ext if (! pp) return 0; cc = pp+1 - nfile; if (strmatchN(pp-4,".v",2)) cc -= 4; // /dirs ... /filename. } else zfree(pfile); pfile = nfile; nfile = 0; } } } // 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 gallery_position(cchar *file, int Nth) { int ii; if (! nfiles) return -1; if (! file) return -1; if (Nth >= 0 && Nth < nfiles) ii = Nth; else ii = 0; if (strmatch(file+1,glist[ii].file+1)) return ii; // file[0] may be ! for (ii = 0; ii < nfiles; ii++) if (strmatch(file+1,glist[ii].file+1)) break; if (ii < nfiles) return ii; return -1; } // Determine if a file is a directory or a supported image file type // Return: 0 = file not found // 1 = directory // 2 = image file // 3 = RAW file // 4 = thumbnail // 5 = other int 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 0; err = stat(file,&statbuf); if (err) return 0; if (S_ISDIR(statbuf.st_mode)) return 1; // directory if (! S_ISREG(statbuf.st_mode)) return 5; // not a regular file if (ftf) { if (thumbdirk && *thumbdirk == '/') tdcc = strlen(thumbdirk); myRAWtypes = zstrdup(" "); ftf = 0; } if (tdcc && strmatchN(file,thumbdirk,tdcc)) return 4; // fotoxx thumbnail ppx = strrchr(file,'.'); if (! ppx) return 5; // no file .ext xcc = strlen(ppx); if (xcc > 5) return 5; // file .ext > 5 chars. strcpy(ppx2,ppx); // add trailing blank: ".ext " strcpy(ppx2+xcc," "); if (strcasestr(imagefiletypes,ppx2)) return 2; // supported image type if (strcasestr(myRAWtypes,ppx2)) return 3; // one of my RAW types 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 3; } return 5; // 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 // .jpg .png .tif etc. // thumb dirk: /thumb/dirk // if thumb dirk defined // thumb file: /thumb/dirk/image/dirk/file.xxx.jpeg // thumb file is like this // thumb file: /image/dirk/.thumbnails/file.xxx.jpeg // else like this char * thumb2imagefile(cchar *thumbfile) { STATB statb; int err, cc; char *imagefile; char *pp; if (thumbdirk && *thumbdirk == '/') // remove /thumb/dirk from the front { cc = strlen(thumbdirk); imagefile = zstrdup(thumbfile+cc); // have /image/dirk/file.xxx.jpeg cc = strlen(imagefile); imagefile[cc-5] = 0; // /image/dirk/file.xxx } else // remove /.thumbnails from between { pp = (char *) strrchr(thumbfile,'/'); // /image/dirk/.thumbnails/file.xxx.jpeg if (! pp) return 0; // | | | pp = pp - 12; // pp +12 +cc if (! strmatchN(pp,"/.thumbnails/",13)) return 0; cc = strlen(pp+12); if (! strmatch(pp+12+cc-5,".jpeg")) return 0; imagefile = zstrdup(thumbfile); pp = imagefile + (pp - thumbfile); memmove((char *) pp,pp+12,cc-5); // /image/dirk/file.xxx pp[cc-5] = 0; } 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) { int cc, cc1, cc2; char *pp, *thumbfile; if (thumbdirk && *thumbdirk == '/') // use defined thumbnail directory { 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; } else // use /image/dirk/.thumbnails/ { thumbfile = zstrdup(imagefile,18); // /image/dirk/file.xxx pp = strrchr(thumbfile,'/'); // | | if (! pp) return 0; // pp +cc cc = strlen(pp); memmove(pp+12,pp,cc); // /image/dirk............/file.xxx strncpy(pp,"/.thumbnails",12); // /image/dirk/.thumbnails/file.xxx strcpy(pp+12+cc,".jpeg"); // /image/dirk/.thumbnails/file.xxx.jpeg return thumbfile; } } // Get thumbnail file for the given image file. // If missing or stale, add or update thumbnail file on disk. // Returned filespec is subject for zfree(). // Optional arg 'ind' is returned 1 if thumb file was created. char * image_thumbfile(char *imagefile, int *ind) { PIXBUF *thumbpxb; GError *gerror = 0; char *thumbfile, *pp; int err, ftyp; STATB statf, statb; static int Fmkdirerr = 0; zthreadcrash(); if (ind) *ind = 0; err = stat(imagefile,&statf); if (err) return 0; ftyp = image_file_type(imagefile); if (ftyp == 4) { // thumbnail file thumbfile = zstrdup(imagefile); // return same file return thumbfile; } if (ftyp != 2 && ftyp != 3) return 0; // not an image file or RAW file thumbfile = image2thumbfile(imagefile); // get thumbnail file for image file if (! thumbfile) return 0; err = stat(thumbfile,&statb); // thumbfile exists, up to date ?? if (! err && statb.st_mtime >= statf.st_mtime) return thumbfile; // yes, return it 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) { if (! Fmkdirerr++) // if unable, print error once only printz("create thumbnail: %s \n",strerror(errno)); zfree(thumbfile); return 0; } } *pp = '/'; } } thumbpxb = get_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); if (ind) *ind = 1; // return indicator return thumbfile; } // Get thumbnail image (pixbuf) for given image file. // Use pixbuf cached in memory if available. // Create pixbuf from thumbnail file if size <= thumbnail file size. // Create pixbuf from image file if size > thumbnail file size. // Add pixbuf to memory cache if not there already. // Returned thumbnail belongs to caller: g_object_unref() is necessary. // Returns null if thumbnail not found on disk. PIXBUF * image_thumbnail(char *fpath, int size) { PIXBUF *thumbpxb; GError *gerror = 0; int ii, err, ww, hh; char *bpath; time_t mtime; STATB statf; const int cachesize = thumbnail_cachesize; // shorthand static int nextcache, ftf = 1; static int sizecache[cachesize]; // 128/256/etc. static time_t mtimecache[cachesize]; // file mod time static char *fpathcache[cachesize]; // file pathname static PIXBUF *pixbufcache[cachesize]; // thumbnail pixbuf static int bytescache[cachesize]; // thumbnail ww x hh x 3 zthreadcrash(); // thread usage not allowed if (ftf) { // first call for (ii = 0; ii < cachesize; ii++) fpathcache[ii] = 0; // clear cache ftf = 0; } err = stat(fpath,&statf); // fpath status info if (err) return 0; if (! size) size = thumbfilesize; // default thumb size if (S_ISDIR(statf.st_mode)) { // if directory, return folder image thumbpxb = get_thumbnail_pixbuf(fpath,size); return thumbpxb; } mtime = statf.st_mtime; // last modification time for (ii = nextcache; ii >= 0; ii--) if (fpathcache[ii] && strmatch(fpath,fpathcache[ii]) && sizecache[ii] == size && mtime == mtimecache[ii]) break; // check mtime if (ii >= 0) { thumbpxb = gdk_pixbuf_copy(pixbufcache[ii]); return thumbpxb; } for (ii = cachesize-1; ii > nextcache; ii--) // continue search if (fpathcache[ii] && strmatch(fpath,fpathcache[ii]) && sizecache[ii] == size && mtime == mtimecache[ii]) break; if (ii > nextcache) { thumbpxb = gdk_pixbuf_copy(pixbufcache[ii]); return thumbpxb; } if (size > thumbfilesize) // get thumbnail from image file thumbpxb = get_thumbnail_pixbuf(fpath,size); else { bpath = image_thumbfile(fpath); // get thumbnail from thumbnail file if (! bpath) return 0; // (create thumbnail file if missing) thumbpxb = gdk_pixbuf_new_from_file_at_size(bpath,size,size,&gerror); zfree(bpath); } if (! thumbpxb) return 0; nextcache++; // next cache slot (oldest) if (nextcache == cachesize) nextcache = 0; ii = nextcache; if (fpathcache[ii]) { // free prior occupant zfree(fpathcache[ii]); g_object_unref(pixbufcache[ii]); navi::thumbcache_MB -= bytescache[ii]; } fpathcache[ii] = zstrdup(fpath); // add new occupant pixbufcache[ii] = gdk_pixbuf_copy(thumbpxb); // (memory not tracked in zmalloc()) sizecache[ii] = size; mtimecache[ii] = mtime; ww = gdk_pixbuf_get_width(thumbpxb); // track total cache MB 15.08 hh = gdk_pixbuf_get_height(thumbpxb); bytescache[ii] += ww * hh * 3; navi::thumbcache_MB += 0.000001 * bytescache[ii]; while (navi::thumbcache_MB > navi::thumbcache_max) { ii++; if (ii == cachesize) ii = 0; if (fpathcache[ii]) { zfree(fpathcache[ii]); fpathcache[ii] = 0; g_object_unref(pixbufcache[ii]); navi::thumbcache_MB -= 0.000001 * bytescache[ii]; } } return thumbpxb; // return pixbuf to caller } // Make a thumbnail pixbuf from the image file. // File can be a regular supported image file (jpeg etc.) // or a supported RAW file type. PIXBUF * get_thumbnail_pixbuf(char *imagefile, int size) { PIXBUF * get_thumbnail_pixbuf_raw(char *rawfile, int size); int ftyp; PIXBUF *thumbpxb = 0; GError *gerror = 0; static int ftf = 1; static char folderthumb[300], brokenthumb[300]; zthreadcrash(); if (ftf) { ftf = 0; strcpy(folderthumb,zfuncs::zicondir); // folder icon strcat(folderthumb,"/folder.png"); strcpy(brokenthumb,zfuncs::zicondir); // broken thumbnail icon strcat(brokenthumb,"/broken.png"); } ftyp = image_file_type(imagefile); if (ftyp == 1) // directory file thumbpxb = gdk_pixbuf_new_from_file_at_size(folderthumb,size,size,&gerror); else if (ftyp == 2) // supported image type (jpeg etc.) thumbpxb = gdk_pixbuf_new_from_file_at_size(imagefile,size,size,&gerror); else if (ftyp == 3) // supported RAW file type thumbpxb = get_thumbnail_pixbuf_raw(imagefile,size); else return 0; // not a supported image file type if (thumbpxb) return thumbpxb; printz("cannot make thumbnail: %s \n",imagefile); // use broken image thumbnail 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 * get_thumbnail_pixbuf_raw(char *rawfile, int size) // 15.04 { int err; STATB statb; GError *gerror = 0; PIXBUF *thumbpxb = 0; PXB *rawpxb; char *pp, *thumbfile = 0, *thumbfile2 = 0; err = shell_quiet("dcraw -e \"%s\" ",rawfile); // extract thumbnail from RAW file if (err) goto useraw; // output is rawfile.thumb.xxx thumbfile = zstrdup(rawfile,12); pp = strrchr(thumbfile,'.'); if (! pp) pp = thumbfile + strlen(thumbfile); strcpy(pp,".thumb.jpg"); // try rawfile.thumb.jpg err = stat(thumbfile,&statb); if (! err) { thumbpxb = gdk_pixbuf_new_from_file_at_size(thumbfile,size,size,&gerror); if (! thumbpxb) goto useraw; goto fini; } strcpy(pp,".thumb.ppm"); // try rawfile.thumb.ppm err = stat(thumbfile,&statb); if (err) goto useraw; thumbfile2 = zstrdup(thumbfile); // convert .ppm to .jpg pp = strrchr(thumbfile2,'.'); strcpy(pp,".jpg"); err = shell_quiet("pnmtojpeg \"%s\" >\"%s\" ",thumbfile,thumbfile2); if (err) goto useraw; thumbpxb = gdk_pixbuf_new_from_file_at_size(thumbfile2,size,size,&gerror); if (! thumbpxb) goto useraw; goto fini; useraw: // use RAW image to make thumbnail rawpxb = RAW_PXB_load(rawfile); if (! rawpxb) goto fini; thumbpxb = gdk_pixbuf_scale_simple(rawpxb->pixbuf,size,size,BILINEAR); PXB_free(rawpxb); fini: if (thumbfile) { remove(thumbfile); zfree(thumbfile); } if (thumbfile2) { remove(thumbfile2); zfree(thumbfile2); } return thumbpxb; } /**************************************************************************/ // popup a new window with a larger image of a clicked thumbnail void popimage(int Fnewin) { 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] = zstrdup(clicked_file); // save clicked_file persistently clicked_file = 0; // reset clicked_file popup_image(popfiles[ii],MWIN,Fnewin,512); // popup window with image return; } // Monitor a gallery directory for file changes and refresh the file list. // Action is "start" or "stop". // Called only for gallerytype = 1 = directory. void gallery_monitor(cchar *action) { void gallery_changed(void *, GFile *, void *, GFileMonitorEvent); static GFile *gfile_gallery = 0; // directory being monitored static GFileMonitor *gallerymon = 0; GError *gerror = 0; if (gfile_gallery) { g_file_monitor_cancel(gallerymon); g_object_unref(gallerymon); g_object_unref(gfile_gallery); gfile_gallery = 0; } if (strmatch(action,"stop")) return; if (gallerytype != 1) return; gfile_gallery = g_file_new_for_path(galleryname); gallerymon = g_file_monitor_directory(gfile_gallery,(GFileMonitorFlags) 0,0,&gerror); if (gallerymon) G_SIGNAL(gallerymon,"changed",gallery_changed,0); else { printz("monitor directory failure: %s \n",galleryname); // it happens printz("%s\n",gerror->message); g_object_unref(gfile_gallery); gfile_gallery = 0; } return; } void gallery_changed(void *, GFile *, void *, GFileMonitorEvent event) { if (gallerytype != 1) { // precaution gallery_monitor("stop"); return; } if (event == G_FILE_MONITOR_EVENT_DELETED) { gallery(galleryname,"init"); // refresh file list if (FGW == 'G') gallery(0,"paint",-1); // repaint from same position } else if (event == G_FILE_MONITOR_EVENT_CREATED) { gallery(galleryname,"init"); if (FGW == 'G') gallery(0,"paint",-1); } return; } /**************************************************************************/ // Select files from the image gallery window, return list of files selected. // The dialog shows the list of files selected and can be edited. // The returned file list belongs to caller and is subject for zfree(). // The file list EOL is marked with null. // The gallery() callback function is restored to caller's function. namespace ggfnames { 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); int mouseclick(GtkWidget *, GdkEventButton *event, void *); int showthumb(); GtkWidget *drawarea = 0; GtkWidget *Fwin = 0; int cursorpos = 0; char **initfiles = 0; }; char ** gallery_getfiles(char **initfiles0) { using namespace ggfnames; GdkCursor *cursor; GdkWindow *gdkwin; GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; int line, nlines, ii; char *imagefile = 0, **filelist = 0; zthreadcrash(); if (initfiles0) // initial files for dialog window initfiles = initfiles0; else initfiles = 0; zdialog *zd = zdialog_new(ZTX("Select Files"),Mwin,Bdone,Bcancel,null); zd_gallery_getfiles = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"expand|space=3"); zdialog_add_widget(zd,"frame","fr11","hb1",0,"expand"); zdialog_add_widget(zd,"scrwin","scrwin","fr11",0,"expand"); zdialog_add_widget(zd,"edit","files","scrwin"); zdialog_add_widget(zd,"vbox","vb12","hb1"); zdialog_add_widget(zd,"frame","fr12","vb12"); 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"); Fwin = zdialog_widget(zd,"files"); // disable text wrap in file list gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(Fwin),GTK_WRAP_NONE); gtk_widget_add_events(Fwin,GDK_BUTTON_PRESS_MASK); // activate mouse clicks in file list G_SIGNAL(Fwin,"button-press-event",mouseclick,0); GtkWidget *frame = zdialog_widget(zd,"fr12"); // drawing area for thumbnail image drawarea = gtk_drawing_area_new(); gtk_widget_set_size_request(drawarea,256,258); // increased gdkwin = gtk_widget_get_window(drawarea); // gtk3 gtk_container_add(GTK_CONTAINER(frame),drawarea); zdialog_resize(zd,600,0); // start dialog zdialog_run(zd,dialog_event,"save"); // keep relative position cursor = gdk_cursor_new_for_display(display,GDK_TOP_LEFT_ARROW); // cursor for file list widget gdkwin = gtk_text_view_get_window(GTK_TEXT_VIEW(Fwin),TEXTWIN); // (do after window realized) gdk_window_set_cursor(gdkwin,cursor); cursorpos = 0; if (! gallerytype) { // if no gallery, start with gallery(topdirks[0],"init"); // top directory gallery(0,"paint",0); } m_viewmode(0,"G"); // open gallery window if (initfiles) zdialog_send_event(zd,"initfiles"); // stuff initial files zdialog_wait(zd); // wait for dialog completion if (zd->zstat != 1) { // cancelled zdialog_free(zd); // kill dialog zd_gallery_getfiles = 0; return 0; } textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Fwin)); nlines = gtk_text_buffer_get_line_count(textBuff); filelist = (char **) zmalloc((nlines+1) * sizeof(char *)); for (ii = line = 0; line < nlines; line++) // get list of files from dialog { gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // iter at line start iter2 = iter1; gtk_text_iter_forward_to_line_end(&iter2); imagefile = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); // get line of text if (imagefile && *imagefile == '/') { filelist[ii] = zstrdup(imagefile); // >> next file in list zfree(imagefile); ii++; } } filelist[ii] = 0; // mark EOL zdialog_free(zd); // kill dialog zd_gallery_getfiles = 0; if (! ii) { zfree(filelist); // file list is empty return 0; } return filelist; // return file list } // gallery getfiles dialog event function int ggfnames::dialog_event(zdialog *zd, cchar *event) { using namespace ggfnames; GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; char *ftemp, *imagefile; static char *deletedfiles[100]; // last 100 files deleted static int Ndeleted = 0; int ii, line, Nth, ftyp; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"focus")) showthumb(); // GTK bug? thumbnail disappears if (strmatch(event,"initfiles")) // insert all files in initial list { textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Fwin)); for (ii = 0; initfiles[ii]; ii++) { imagefile = initfiles[ii]; ftyp = image_file_type(imagefile); // must be image or RAW file if (ftyp != 2 && ftyp != 3) continue; gtk_text_buffer_get_iter_at_line(textBuff,&iter1,cursorpos); gtk_text_buffer_insert(textBuff,&iter1,"\n",1); // insert new blank line gtk_text_buffer_get_iter_at_line(textBuff,&iter1,cursorpos); gtk_text_buffer_insert(textBuff,&iter1,imagefile,-1); // insert image file cursorpos++; // advance cursor position } } if (strmatch(event,"delete")) // delete file at cursor position { textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Fwin)); line = cursorpos; gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // iter at line start iter2 = iter1; gtk_text_iter_forward_to_line_end(&iter2); // iter at line end ftemp = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); // get selected file if (*ftemp != '/') { zfree(ftemp); 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; } deletedfiles[Ndeleted] = zstrdup(ftemp); // save deleted file for poss. insert Ndeleted++; zfree(ftemp); gtk_text_buffer_delete(textBuff,&iter1,&iter2); // delete file text gtk_text_buffer_get_iter_at_line(textBuff,&iter2,line+1); gtk_text_buffer_delete(textBuff,&iter1,&iter2); // delete empty line (\n) showthumb(); // thumbnail = next file } if (strmatch(event,"insert")) // insert first deleted file { // at current cursor position if (! Ndeleted) return 1; insert_file(deletedfiles[0]); 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 gtk_text_view_set_buffer(GTK_TEXT_VIEW(Fwin),null); cursorpos = 0; } if (strmatch(event,"addall")) // insert all files in image gallery { textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Fwin)); Nth = 0; while (true) { imagefile = gallery(0,"find",Nth); // get first or next file if (! imagefile) break; Nth++; ftyp = image_file_type(imagefile); // must be image or RAW file if (ftyp != 2 && ftyp != 3) continue; gtk_text_buffer_get_iter_at_line(textBuff,&iter1,cursorpos); gtk_text_buffer_insert(textBuff,&iter1,"\n",1); // insert new blank line gtk_text_buffer_get_iter_at_line(textBuff,&iter1,cursorpos); gtk_text_buffer_insert(textBuff,&iter1,imagefile,-1); // insert image file zfree(imagefile); cursorpos++; // advance cursor position } } return 1; } // See if image file is in the file list already or not. // Return the matching line number or -1 if not found. int ggfnames::find_file(cchar *imagefile) { using namespace ggfnames; GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; char *ftemp; int line, last = -1, more; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Fwin)); for (line = 0; ; line++) { gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // iter at line start iter2 = iter1; more = gtk_text_iter_forward_to_line_end(&iter2); // iter at line end ftemp = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); // included text if (strmatch(ftemp,imagefile)) last = line; // remember last entry found zfree(ftemp); if (! more) break; } return last; } // add image file to list at current cursor position, set thumbnail = file void ggfnames::insert_file(cchar *imagefile) { using namespace ggfnames; GtkTextIter iter; GtkTextBuffer *textBuff; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Fwin)); gtk_text_buffer_get_iter_at_line(textBuff,&iter,cursorpos); gtk_text_buffer_insert(textBuff,&iter,imagefile,-1); // insert image file gtk_text_buffer_insert(textBuff,&iter,"\n",1); // insert new line gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(Fwin),&iter,0,0,0,0); // insure visible showthumb(); // update thumbnail cursorpos++; // advance cursor position return; } // remove image file at last position found, set thumbnail = next void ggfnames::remove_file(cchar *imagefile) { using namespace ggfnames; GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; int line; line = find_file(imagefile); if (line < 0) return; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Fwin)); gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // iter at line start iter2 = iter1; gtk_text_iter_forward_to_line_end(&iter2); // iter at line end gtk_text_buffer_delete(textBuff,&iter1,&iter2); // delete file text gtk_text_buffer_get_iter_at_line(textBuff,&iter2,line+1); // next line gtk_text_buffer_delete(textBuff,&iter1,&iter2); // delete empty line (\n) showthumb(); // thumbnail = curr. position if (cursorpos > 0) cursorpos--; // backup cursor position return; } // called from image gallery window when a thumbnail is clicked // add image file to list at current cursor position, set thumbnail = file void gallery_getfiles_Lclick_func(int Nth) { ggfnames::Xclick_func(Nth,'L'); return; } void gallery_getfiles_Rclick_func(int Nth) { ggfnames::Xclick_func(Nth,'R'); return; } void ggfnames::Xclick_func(int Nth, char LR) { using namespace ggfnames; int ftyp, line; static int pNth = -1; // previously clicked file char *imagefile; int control, shift; int nn, incr; if (! zd_gallery_getfiles) return; // should not happen if (Nth < 0) return; // gallery gone ? imagefile = gallery(0,"find",Nth); // get file at clicked position if (! imagefile) { pNth = -1; return; } ftyp = image_file_type(imagefile); // must be image or RAW file if (ftyp != 2 && ftyp != 3) { zfree(imagefile); pNth = -1; return; } if (LR == 'R') { // right click, unselect remove_file(imagefile); zfree(imagefile); return; } control = shift = 0; // left click, select if (KBcontrolkey) control = 1; if (KBshiftkey) shift = 1; if (! control && ! shift) // no control or shift keys { pNth = Nth; // possible start of range insert_file(imagefile); // insert file at current position zfree(imagefile); return; } if (control && ! shift) // control key { pNth = -1; // add or remove single file line = find_file(imagefile); if (line < 0) insert_file(imagefile); // not found, add else remove_file(imagefile); // found, remove zfree(imagefile); return; } if (! control && shift) // 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,"find",nn); if (! imagefile) continue; ftyp = image_file_type(imagefile); // only image and RAW files if (ftyp != 2 && ftyp != 3) { zfree(imagefile); continue; } insert_file(imagefile); zfree(imagefile); } pNth = -1; // no prior return; } if (control && shift) // both control and shift keys return; // ignore return; } // process mouse click in files window: // set new cursor position and set thumbnail = clicked file int ggfnames::mouseclick(GtkWidget *widget, GdkEventButton *event, void *) { using namespace ggfnames; int mpx, mpy, tbx, tby; GtkTextIter iter1; #define VIEW GTK_TEXT_VIEW #define TEXT GTK_TEXT_WINDOW_TEXT if (event->type != GDK_BUTTON_PRESS) return 0; // reworked 15.10 mpx = int(event->x); mpy = int(event->y); gtk_text_view_window_to_buffer_coords(VIEW(widget),TEXT,mpx,mpy,&tbx,&tby); gtk_text_view_get_iter_at_location(VIEW(widget),&iter1,tbx,tby); cursorpos = gtk_text_iter_get_line(&iter1); // clicked line showthumb(); // show thumbnail image return 0; } // show thumbnail for file at current cursor position int ggfnames::showthumb() { using namespace ggfnames; int line; char *imagefile; GdkWindow *gdkwin; cairo_t *cr; GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; PIXBUF *thumbnail = 0; gtk_widget_grab_focus(drawarea); // GTK bug? zmainloop(); // stop thumbnail disappearing gdkwin = gtk_widget_get_window(drawarea); cr = gdk_cairo_create(gdkwin); line = cursorpos; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(Fwin)); gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // iter at line start iter2 = iter1; gtk_text_iter_forward_to_line_end(&iter2); // iter at line end imagefile = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); // get selected file if (*imagefile != '/') { zfree(imagefile); cairo_set_source_rgb(cr,1,1,1); // white background cairo_paint(cr); return 0; } thumbnail = image_thumbnail(imagefile,256); // get thumbnail zfree(imagefile); if (thumbnail) { cairo_set_source_rgb(cr,1,1,1); // white background cairo_paint(cr); gdk_cairo_set_source_pixbuf(cr,thumbnail,0,0); // paint thumbnail cairo_paint(cr); g_object_unref(thumbnail); } return 0; } /**************************************************************************/ // 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 GtkWidget *bmkswidget; zdialog *zd_goto_bookmark; } void bookmarks_load(); void bookmarks_listclick(GtkWidget *widget, int line, int pos); 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 list position. Click thumbnail to add."); /*** _______________________________________________ | 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,"text","bmklist","frbmk",0,"expand"); zdialog_add_widget(zd,"hbox","hbname","dialog",0,"space=5"); zdialog_add_widget(zd,"entry","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"); bmkswidget = zdialog_widget(zd,"bmklist"); // connect mouse to bookmark list textwidget_set_clickfunc(bmkswidget,bookmarks_listclick); bookmarks_load(); // load bookmarks from bookmarks file bookmarks_refresh(); // update bookmarks list in dialog zdialog_resize(zd,300,300); 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() // 15.05 { 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 15.05 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 15.05 return; } // mouse click function to select existing bookmark from list void bookmarks_listclick(GtkWidget *widget, int line, int pos) { using namespace bookmarknames; char bookmarkname[32]; if (! zd_edit_bookmarks) 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 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,"find",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 15.05 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,"escape")) zd->zstat = 2; // escape = cancel 15.07 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 (strmatch(event,"enter")) zd->zstat = 1; // [done] 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; wclear(bmkswidget); // clear bookmarks list for (int ii = 0; ii < Nbmks; ii++) { // write bookmarks list strncpy0(bookmarkline,bookmarks[ii],31); cc = utf8len(bookmarkline); // compensate multibyte chars. 15.05 strncat(bookmarkline,blanks,32-cc); strcat(bookmarkline,bookmarks[ii]+32); wprintf(bmkswidget,0,bookmarkline); wprintf(bmkswidget,0,"\n"); } 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_listclick(GtkWidget *widget, int line, int pos); 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,"text","bmklist","frbmk",0,"expand"); bmkswidget = zdialog_widget(zd,"bmklist"); // connect mouse to bookmark list textwidget_set_clickfunc(bmkswidget,goto_bookmark_listclick); bookmarks_load(); // get bookmarks from bookmarks file 15.05 bookmarks_refresh(); // update bookmarks list in dialog zdialog_resize(zd,300,0); 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; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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_listclick(GtkWidget *widget, int line, int pos) { using namespace bookmarknames; char *file; int err; STATB sbuff; if (checkpend("all")) return; if (! zd_goto_bookmark) return; zdialog_free(zd_goto_bookmark); // kill dialog zd_goto_bookmark = 0; bmkposn = line; // get clicked line if (bmkposn < 0 || bmkposn > Nbmks-1) return; file = bookmarks[bmkposn] + 32; err = stat(file,&sbuff); if (err) { zmessageACK(Mwin,ZTX("file not found")); return; } gallery(file,"init"); // go to gallery and file position m_viewmode(0,"G"); gallery(file,"paint"); return; } fotoxx-15.11.1/icons/0000755000175000017500000000000012616075370012771 5ustar micomicofotoxx-15.11.1/icons/save.png0000644000175000017500000001176412616075370014446 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@^[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-15.11.1/icons/redball.png0000664000175000017500000001241112616075370015105 0ustar micomicoPNG  IHDR@@,IDATx xTǿ3K2HBXjKT@Z`ZExl*j BQ[ \P. UF\ PD!~g763,y[9T??HDiCVHDߧܑ9D-h$C#uJk%L zGY`pP1< KcABPI;Q0J`:=wS,0y;t>K=`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-15.11.1/icons/sync.png0000644000175000017500000001013112616075370014447 0ustar micomicoPNG  IHDR00WgAMA7tEXtSoftwareAdobe ImageReadyqe<IDATxb? 0f32@5i@5&@\%&L} ?&3TӐ@m!dS:TD  PxG@$ZnJ xfdcаee9߿C@ՎZ@֘?tWmctNf`׈@X@'2o'88eaXSAO@$xbX9`U z@ X; 2 ӲN^8Tu Y`#0:]bH^#r 3`01UGxuR0,۽؄s##SP9B C bbdeab8\7к?C?_L gv1b|Ϟϰ`:$CϪ L*;QDԀx20@#Ci,52+3{$?O &/2Yę_P7Ai#7P\F#5/0Cc1'n2Cɠ 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-15.11.1/icons/up+.png0000644000175000017500000000151312616075370014176 0ustar micomicoPNG  IHDRw=sRGBbKGD7A pHYs  tIME (IDATHǽKQ?gf$ybY/KP t_sDA{JDs+¢2g֝aϴggM~̜?~D?)ufbso(_`uXGTëp?50Bq C!x<7>DUeE'F }sG9MX-؁ qmwf] Cؿ"1%+U|\Kh!F!V" ੁvuRc黣M,,Z,g}ҽ>k`R0ǣ;[I6mbiFhAg{+{.ڦ;`k/C͍hPa٫EoisrNFzԌOd1;)U,;@CdM%B!|)e&86GJi<8֜`4~ R"O@{fbEF^fXm'oZ~ ֞7)TM:~u^R*%,2C2/"e-PT;Ж6 u.(*bJILG)|5_]vVy[ڷNph(F"#:^_D,r%!ɊR73\5O!eނ{~~$BNIENDB`fotoxx-15.11.1/icons/cancel.png0000644000175000017500000001374412616075370014735 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-15.11.1/icons/viewF.png0000664000175000017500000003620512616075370014567 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-15.11.1/icons/blackball.png0000664000175000017500000002030612616075370015411 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-15.11.1/icons/albums.png0000664000175000017500000002406012616075370014766 0ustar micomicoPNG  IHDR@@iq zTXtRaw profile type exifxڝm({@Bq\otLlUvL׳HGk69k瓟O|]ꖾO7c_̞/}@ wgg|Np73 &8%ZjJI"eK&ITq^Ϋt~܁w2^L|o$"ONuo;RRkғ @탰sTB~rg?`{9\n-8j'b~ϞVJR. kXgNz< y>xκ)ʷUnYb39ӷ_579nKyh7=;p   /﬏O+ =l Ԕh;ڿlP>׋eG5_8T 4kzqpBDr u{3pj?D*) ĵD[Oz)>k*14xY=ڶQoӫay|:%O0|XAj9:EuuwA#K~N2;246SZ"1g+xn"B^ؙ &1'z ZwZB  noC#=8( )*gk  d/O-{#t˘R,(ED.!̛*pL1Rp0>=pk.s$rŎUIKh C$U4ɩ/M\FYH-¡ dV1r c{EjGШbɢ8k+]\}_]@(& *¢x)&l6jR H m)V|K|' !R 8v.u4Z*ab& +hc Sk:|tPf !%A`ad9Ñ{%>^ʸB2D![} H cB>0f̶67J@8`L-W.&HaTo7!V| ]v1;@UOˏjwYmrx51aȼL{(k/"Ն/TCQu]~!d Gm\z؀p~gqC*I^xQ5^Kjrpwp'TNH}|AlREF`RQѬy ~ x5?X"\`:.`Pc4Nh|fJ 9eȖ!Z2N#jhADu]˃6x9oҮ,EUbQέy~$`Xp;  %"ԗUJ*#$4#.O0dk 3CƗVsBIT|dIDATxśy\u?^ڗU.!@ dfcYH$L383vē3$x` Ȁ1MtZoooU- ۉӧ{\d C?t(P{#;nD(zHcBbݚ$x2W- Tj>ER~[1zn8o|nԓZ25S x`P̉sgnOR0l(! lO~w#B>9t,D뷦(,{ +2d{?!\\gH)}V|ˉWʨJ44k5P\s$u J 8͍xnXiu}NRʵG^{d<;OPPLPTP^"Aǹ]37U %] މ'̷>77J>y\IyN zz_UJ Jg~n57U_~`^Zuï?}qyUf)[oVx?% Deqz_!?ȞGlػfrC~_^s wvwv_k!@U΀!@%l.!?57p[kBÿ,6$(UKϟ|xa t=P;Bxj i> s 4?+qaB}EO͆ Yw-K~]J_ ׵n;gN>BstQ@$1T"5He@ћwD)Ҷi3%ʍE?+]uBz.)ej~ls_OiTPl׬vZJ%Iۇ3xHzL$?oeGie$lӪNpݵ8Ν>(wQ~ǶՋ<.*90?\q3շC$bcCϒu/S -HZR+_K^]JB`ԉC_:#Qo44vQBarZFcK 0jtJsjVuI+L~' BBNA:xI2hc/LfokRʯ/ HH)7M< ߽߳ 2BȦmQVӚ+`&ߍMr(?q2"]  ?}D'HvDZUb+J\qoM5׽zݗ4MB Ro}{V*UZEl\p]!7ފ{ h4}cc T$$u:5C*Sk>ڙ^ܫ8Vh 4r>+<2F{J8a)s;Y)叅s_\|vb"Jv;Fl 5bYGΰF݆<%BnP I6i AE8(qr5NշҼm6Y 9I8vjyI2 N_)깑/{ e.y.");xf eHe,¬8 H$iIBef6-eNkQ5U;wγ n HBQ/)ߨ˻g* Py{w](Td'Y!TX0(t%L&0 iH\jB;!%qq \`Pߖ!#xW<$(:eaE*bvﲱ;Ţ-mHFrTe g6w*ufAƐp2'-FFhu7'e S _m5##tWB{@JQƑgcCHFmuk k2aEP,l WFk 3 L؎l~E"-aK1M r =KҮ#|(2Kl#shv!IRJm6>s\1{Y!d!d3ɷ3@J:6_}+;E us&Mo['aP4 &I4 W!WxK1Vbv)ZP .h;sR_{@$ g dn{Oss` gz6}5=4EdMr"g18 T5i4&yHMR9_| 8jYHjI%3Ź_g2<~O?lŠ8W^'J%Duu8Ut92ݠd}*A_wZC񻨞KKqSS| Pt?gN%_m{ײn˿u3 1Ӥz_ :N~BŌV$g K~UkhM-k2IC85oaWնkm׸957eP1* ND'(z <\>6+a_R.)e,0mxYXM*_!8;\w6ucE>: 24BJ+]c)y|wUPK:Z)R:H ʭVa8J~*W {9:~޺ڋ,VͿ}ZH/e3[q=kٸxnX rgǽOCJpbQ˲H)A4v(K5 Fs)$!#`FY ZS"ύ@5̢>+ ^b;*Z]ނuLC8 .FUAJbLݰp,BۿUW'oeӪE+(5>T+Sa.)B5K R7K@Q9"=̆x%t"U5T=[\ @q bvŝ ; /-ʔ Mg.(>(xz0{V3ՖoHi!:z8D4Ԇnc?>Q75}~S#%/|jC~>=>0a. `+B@qɏ ^0nU#xZyA=YjK+{5QtBe4IlPB1r4/0$iyZTHuI|t'Rc_J6Ja0Ws>3?h˗y"AٰOB[Rnڦ^n~~!/x&. eOQs:xp}ѐx6@_$(̝A5l\o\϶uh~3(ΓNH,^JUgQh% l7F?wF+3"HxN3'mO¶E]BCS&fǣnP|Q@ZT+ֶq],O)LA..hb,ёj#԰0FGH0Qf@&Q)~P4 H)X$x#SCĸDtl@pl@\ pV%lRV5bwK(qƆB_ĴcuK`|[WpɊ4P2`6tIS5(NJDQ5=lj }45g9 e~+?/ŅRor3{mCoš\EI !%P8B:. %^p&M/J! JDO ?QbXK:غx"Bfra2$z6S$B~:{{0=ѱ)BKOg"9p1߼}6"erGzeHaI)K7xhwpp  XH!Crm tϥ< {$rơFQ gR7;RBѹtiפta8rq,[-abrғi&gNlG;Z8SpCdmfٖhK&/8kV?oHcs/+n ‚hZתDܦxVIb디f) _b*奙 p0['I"TS#c Yt1BՙÇG6Ӊ0/5L' ǻ?̩ݏGAW;DWlgeۍVL&B̼䤔mۛΌNŃ'VQ$UVe –l s y=K31PP^2\2Ҟ6sy&}*]0|id:cmJNst5$qXC;k.tŊhBix |>_'RIUV b56A$:<碹E$$ /mdzi;xb8x.lp,NRUiOOԊy 9N(呧h剫q\o%,۲[L&;~ذ,+s{ǔҙ&fY#fmy.!‘y(U5Z"7J1L LAggmvrXTg'R#f3XnSG~ݴ)Ӥ|a o[sɆl6;͟w{StYX;})FXB2:&|ݬl['U;:011IR="-PLA~>Ote;Quxs΀GMnV]i`0P(s@U*ϼ߽O:BV"aIaAlh|0Needv$or9lOJwowϳ?yYKyfQ%G2wuz޺n B2Hj ɶ8tRqû2j#l6_Q[ldp88~QXV{v?#nTI i0f7oaeLZN[[T*E0(D#a24G6@@V[t\Xf,u<X1n}XF8_(~sS{n'TcflF@[e]HqBH@d;qsF%h+^Ɋ׸+֬U\, RO~:/~V SJjїL|20;;te;5034 1Pݷ`fK6pWx& ]̇//:h,2M_Tv0#E{X@ @GG;ѰL 5LWX#ş ;Y~T^$U]t,X09p_um_Of_(T0LI"g1hÒp=y)=?Ț {F"g )O`RJh|jhdv=5+CxcdC&Pz|#db!&eWx%?&<vzvVnVZxɞD"qO4~} P(|'xC ! Dj֭[7nܸyf]]N/3i37 Ύ(Cwƀ^!V{ .\rٳn!0&DͿ^M~~~@ `, !G=}tkk+M 㙚ZZZV۫P(={j XC5jAAAov%> 8ק"B>p1bq^ah2dP(T(oyeY!B[l!] :;;wJ<~Z0tڴiVVV---@*J$ccce9S(=z„ X'''O0_HxAO8u붮.EӴOKKKii Nlrr5krssRMJJJlllZZZhh(˲]]]&&&iii󟛛9Gv9s&EQ,˾u7cLdacccxx :1֬Y- JKKg̘o>RT*?~|Qjjj=z4j(FdbbrIPX\\\WWw…#Gܺu۶mӟfŊΝc(CJA7oR5|>_*uYinn޸qӧ:8NRDe˖iZGGGF5AVX-d2ٹsVX{3f̞= BDQVwJ!Eǯ\G !011^|D")..޾}C*++x<˲AAA B*?~\,H񂃃?~uȑ:ggWfggFFF`;冼IxuPSSSXXXF)1h„ rss#""N:UPPqOEE`ʕ_3g@"D",//,//={6xٲe홙cƌ(ܹsӦMcutt]#^+Y+++9sfiiO?qrrjllLJJ$>}9,, rpp裏 ߻w;|~ZD{b@ ŕ]pA,O8B1vڕ+WȊ @Ga^A?~… Fjkk{yHH0C Zٳ77Wt3 0:1cBAAA'N655 >\v׮]O^Co-[ܹʊ<~8%%y_~%1]KKˉ'rqTOvbc0<|xttV5 S=~1>˗w% A;w̛7Ν;Yf-^Q cqSSөSBї.]Zb833ИjHzꐐ< L=zt۶m$ZcOO [NT\reoo/EQJ #eX!0:PZijjjnn8#@ |e󣣣y<^RRҬYO*@ !ruu3g q>cc7n_m@8&z%AË/Ν;!]zJ*//i֭$OXɩyСQQQ[b'|!yBb\R'%%UWWD;v^v B!\x1cB: B1gffgffvɒ :tHT6l W5}bKo Ƙ㸌 wwwL1vpp U(EEE]]]gYB{yyinJEcy<޹sڼjkk8o[@U*VFUVV.\3$$wӦMd5kcZVc5H<(&%clddts* QV۟={'Nh4B 0Loo lmm FcO<9777 ((UUUcP@ B@RJeCCp?>ޞXQQA 0$QuuuZTu)ڽ{Z^tӓ|>[j3V?4h9BbqSSBHO$|CP1 z꯾*''28.;;'88xժU֭H$Gj|gWEFFFqUUD"~]]O?pBrqknn^UU:x{BZy昘}"...6m;|pkkkMM?qgnnHRȀ@"ƚ77<s]VQQ!`? 'z`inmmOIId0tʕ#Gktcc㜜0MSwvpp 3 AI] !yNQ>}kdd4}-[XXXx{{q1),,LJJZfN3< lll֭[K<==SRRzzzrssRijj*cnݺ)S:rJPvcB0h VN… ܹ>h 1oiZ*D"77aY6''gǎ&&&NNNlqwyʰg϶;yyy[nڳg/0::jڵd2Ycc# 8p`ԨQݛ7ojÇYe3ּw0Z?11,˒q߿ӵ}}}7oVG|8+ʬ,aǍ;̞={хYYAŋɫ9_`Y!gaaNQH޹B9r_J|r ݻwO*++ | ƘpRTJvÇ,[XX3@p ͛gmmlٲI&%&&VVVrGQTBB† ޒ{"dY[[oܸa!EOFEE1"!!AP{ =~8Ƹ燅1 sĉ3fbJ;zzc ,--;nߔy 1={9s 9?R7''':[[۪tww'N755`1'OdY6///??&رcMMM5Mmm~XTT{ׯzzz\Gb6xIs[?(em۶=O?%_߿aÆ?=ztCCyV__OӴB +00c,.^WPPsս{BW_Ȱ3d-1q_VV`qA122*(({ɭ>>>2Դ3//MMMs=yҀW|-[$ BaRy k׮a 0 D!oos/T*wwK$kkkrJL&-..(j„ ՗/_VEYZZ~պٳgA 0 ^eo:c6lԩS際:::N4MؘUWܼySPp/n1lɒ%QQQ仉w_k ap޽Re1"@ joo'UJdMM*A1C]hadd!C0{0.]JJJzB@ !D6|$v'P0iҤI&x<~!/Zz@㊋Ϟ={䀅!!Z[[?~Ɂ =ddחUUU566>{\ՊD"Æ9H$777?o-QK>0@DB7=(DD߮WT~ E`?|o&/zTXtRaw profile type APP1x}QQr!9j'3umxUs.( Q5[%}y`nu8(TЋUR"$#|=#CLRd 8 1000 1000 2 2 0 2IENDB`fotoxx-15.11.1/icons/viewG-check.png0000664000175000017500000002620012616075370015635 0ustar micomicoPNG  IHDRPPse pHYs   IDATx^Y-y-YkV[wnHJcKu4D a<ޞ ؆a@ / aGCIgDH[}S?yp(b_}9)`I|`8MȐl"c¼`3B#h4Ys3^"aa|f<:JǙE΅(e6+>jfއfTAcVcaIM# 6e!+k7޼{ѽ{{O=x჻O`+r r8ib.%SfAA0M J@E^.=: rƹd+mlk;)۾>^T GKZSfeֈ뛍;f{[|#֧~sŴ۸j r&KSJ)%Dz H۲:%=dB$2(D @IADH`ӷ9͎NwoU,燨HQ)o{v,b$Nn_ g!uX.k{b4ߩۛίwȬ wo/1$%@ D$P B4wV,gzY \LNp (E R"EF@p(lB WwoV^XkE`DeDQA@;[A`l^|e3f͙+ʕ͵,wK#AMjbg:gB:k+yw䠍peoşϤ+;OC4 "$FP f׶]dbYurTj2!+O֗|&6>yI $ "hvd])o_t8=uud)j~_}Vcߐy'!dxQw˙?>Z<;;8}Jz5$~ʃicıI0+(6!;P!&s/G\ ѕh2 2(vU3AAgO.|ō-m-iZ 2Xiu&g%;`J5m-h,γbfkHSOOk~ã>3 (@P T6bӘ aaۄj]$4x6Y/+뽭%o9*"BH-CV8V;kk~UMpZk e2߄K?eSAh^KǾjkBN,ܾrݻUBdS׆ YM(lv_~J%mT2[MoE V'>T'EM ` 4B|vysƅFM$Q6}˔((_]rZ,4'gjh&WrI6v,. B5)30XHtI'G]Ya $*II#X໦ۦ7 ;W 9Y  1 ZE]o5t*9/&Ֆ( &)DMA/G=+m^c3Ol:K-Fm Gq2h@2w_EG zVDDC( HII(!@9W6T g1) I $@h9&DEv: ):x( j.&+]Q{|$M"$·I 86)E Aod?[h.<P@P+`Le аЎ{/ "Bc "Xƃ29p`QA3?@ ж %1l b=E@T 1)й%ltݗ܏[7O.\|5rJAC&vI< ! }f2sݴ}B$F(QߟME+H^4 Γt8s` -3s$ UA%RR*j>Y|~~Wb"7o}'ŽBӒ"!wIۦ+aR5"C Rx&ܺ.0OalWl>s.y")72>Dhc5 ,"lP$,[fV&b%rZ1HRQ"(?W  4ߙ<٭pYD} ȌmED|>J)z9-z9d.h(L`:cY6󰘆$M] d6+Ժ(`ٲIξV\v o@Prds硛tZug6ZPmJhtQL`4[ VM0(:/r?tXfD$IjȽڵUɊ`4%Q ^D1QeMv^'Û/_RT|'mc jrW6/z }aO뗿ٍvV J𭵏kˊPM 1zg@"C+ 1h̑a#Y*'usvVg)0d@HB-T:TOL 1΀1@̠A`kWfa^v&O#UW=^֯~iyp?ṁ,{)3UՔͲ:rg6VW6WGgn|{q6]b[p `q( I<ևi^%EPǛ㡉E KJ.[o}o|Z%GsGG;lnn޽{woww1%BTlRd4*rL ) E[cfQQC6+Lw j^/#hc-jj;h V0`XAPTBBEHIuL]_Rù)3z_߽nT۴b~)`~x ]{q曯բg'ҥ*ɋ4 q`b)Y_8,ʍVb7YxϪҷ*vЬfo.^//ZRHCLpBlE+U0LlR5SUSGq</v_~!xWޟGGx0(1PM`ؖei0FH@) k#.nF\LAM沲XW/7N.^݇$0I$ȁcUl߭EsŘ@'ŗԍ0 $}'gq+J-fsF+5[kffLr913zd+y1%@`FZϞ|7;+ol o *(Ad~VΎIb]KB'k{8EآgO4r1ygtzPh^"äI].e!`MՄ& $厓l "pك0=yaZ9K*ݬ_{m?tO}b{C񺆐1.nyZx+~ηi~t~*$28l_B1ƤC(2g1h40a{ %јՐ2H!%Ug7oao[$^xnm}_wn.MYQ1H EQ;mYfi<1}[G  enhBHΰo`b&g>5IC) 6SYz'<}q;W~0!kWϖa:;;8|̦  UUژ$̈lyft1^? =eIR.c,rLj lz{Y kRmsQ:tW^Pswnӽ2Ol>wQD 5gβwf3U-(;(ιcQhtڵ;Y]bG"5En ׷/D%A;md\@w e {b^k֏凡=Hw?͞~\zY"gMw|rxr2a~Ohټ(h6vDUWUctQ"/%RԴ\GO W8su6]X˶R6'vx(l"l 6p[tYKW7ǗHZrtDLJ: `b8ZL"ɸ<+:SJComYZݽf&AҲY j`1"kQj'_= H &[t'A/'rfX*b0DLjXk=٢?xbG ޶j޾Th4y]&tໆQ:Iiy 36H횥Tyq#%^;gEoԇYl565' ,ex2{0άɖ˶y5un\E1cF7ڇ~QV6ܜ6JI8 7.f5KwRiɡ>=OaN-|foet8VRJlȘ 09猱1%U]]]mcL^G>?7^===m9cAo A}o7D޽oQBζH(^KDAPyS8%IJ Ƞd:=/7N 'goCzU1fbQV-R/& 3fQ/Lc+>kղ1(n$T5tQ "1_xş#$:ǎS(D$)RHPK,Սǟ~_1GHAWkMn{_o_wߟS'^*ZP" $@ظp@+@hqccePXJ☈*̒$]Q4QQEQZU%FzB|o,1!$DTPUMJ SGh87̉i%ZVyKgǜN.;ѯ|A @ @%8R@8kD"ʊT!#hP@ ԫBM+ES dHړ(}6"ZF$@3]kwO^˼$ C_q|שCK\tM9滴 ((|*D1sV$t]}k PJJQT* wҫ(b2Zj`H`BV6ePVIi|~q?oAe{~~oܺ?xWK2ds 2=DL%) cLE666~w~kI їwS>[gccN>J1FHb-ei w]-s[ossopk~عz1޿stnaؐu=?ϾfG3D|m{m} Dlɟ{+|%\?7 Uu?'Q'ɜؽ# K*ڮijQ溚$!Vo>1tH@$ ^fˏrewUGQd,C@d,2NU.IDATu-_޼j>tN}7U $D`k%iy("b1SUc-h"sn0gEywohhz )e-mS-aAC{eϲ3GHE1Gwkr`v?έ%4Monm=?BI5H|K -S:̑s|4E&v$G%,B%$%"1. ϫ7\rT`~ # E6\m̢k u[ݿhTؿgZ .\ܮ!q2ʌ" d|fY6 jHI070(hoKfN u #(cl6YL/\{Gv"y5ʚ̲̤n999=f#٨\+˾g(3`1bҞO.m=ƨ/}[ /<w,ٝ;o7Tq a0tnEDXDHZ38g,XNmH%-,$0@UEAij<+nҝ՝޼P|2XšԵJƱ:IhN'eٟ _}c#k潦@J$HJE @+ddH4ão`G1I*kr$& Jdz&%)IU%@v^gYuV}nԶB"Q}h羮gEӴRcXt5ԕo}(hq|+Dt>jE9h:99>q*1wSO E彲!ȜDt>}Cm\ ,j/ Fr>gDU1u>>u*+ 2p=j6!ve *uY]4rݫWvz^"@f/~s7:tl 03` $@J)Cu1F"Ϲj‘a >b॓.$ !4J" ks>&l*iUʼ4pћZdp?W} a"7.ϼͫ/W_}!Oq[ۗo=8\{. BH3&JĨ)H/1ZAR$)"+/Xu"ְc0.elbvFaQ.jddl>嗓c(rt3J|t> WMcTA 8DKd@P1,I@ч]ǘ"еKH(^Φf93 9TF*/fHƸ`TfW$ӿ>ܲvyg#W8|ϫKQ)%%ODB5d+k+> H7./1C̨cRdPUUE`@P@EUH RRIpKּW/v0e]Ȋ0xNS̥40 ܅0y`L˺ "*D``-!&H^sP|X3|)*c$UBRff"FBDDr2 h=t 4uefcQUa&D# ̘^ % 5bR0$PX0)EHqoJi%H JmIISH ZLDz~^e[%!3 jJx@Dbt] 48Fc3P@G\?Q׏cuߦ/U)i/tIME - AzTXtRaw profile type APP1xeR[n0)`l'8JU[?&]$ag_5ܚ5hLي+,4$J813B,-䰉u$$s|ثd5u-go8ᚨy! 0;["v :M>y *tV- I,N3dg>4>|沓&Ѯ][)~mI}唱$2Np?Px?P`w۬^\_mR"z<.x?7\Aey.iTXtXML:com.adobe.xmp 80 80 0 \IENDB`fotoxx-15.11.1/icons/up.png0000644000175000017500000000140012616075370014116 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{"o]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<²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-15.11.1/icons/rotate-left.png0000644000175000017500000000557012616075370015734 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-15.11.1/icons/open.png0000644000175000017500000000422212616075370014440 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-15.11.1/icons/folder.png0000644000175000017500000002352212616075370014756 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-15.11.1/icons/setmaprange.png0000664000175000017500000001523712616075370016017 0ustar micomicoPNG  IHDR@@% 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#aIDAThCZyTW.nh}(5uɘG4,9Q$y/8dN2LBr3qd扚qE6Ydhn)K@ɜ33K}{~X?;Qa* }Ocj\ϲ,0~0LFHsyL2Q544g2, M4Med2Z4$&&&%%EGG!Q~rbR/ F@0 ytvvv666677AReeeeggeggGEEˎ=xlk(~hѱC0 0 Wq<,L?uӧϞ=;..N.Yn30bW@@@xxXBBbLLLX^&78pX,fsOE9ygiڜ'ϟh"^H?Nȑ#Fitdx\\\qqqvvvbbVUT$IbD0at:{zz.\PWWGQN^1cƺu벲pBbڵĉv'^://Oד$Iiy(쬬8q5J:(33fZ,C4}f Gqeee55hI .T*c;wX,aӉb !!!!,,LՊ[(b``ٳv IŪU֯_5f2ɴ{ZArٓO>aÆ3f:d4\.7˲q$u:m|||bbbnnnaaatt`"##ouرۣq߾}{PP8l q6w=}d_͛74]__ԩں 8srrf͚`(Q`xxo9xPoo/T^xA_?A`YvGeYði[n2et,o߾˗ "]GP(`ǡ zfCTFEEk$$::=/0}}(Z+Km*$˗= 11W^$B[[ݻKn xLL9sN͋QA@Ȉhxbmmaxx…7n,)) Biر/h޳gOBB^J@2۷oGj7nꫯ* kjj--^@#ZSUUrGGGYE *41|!1;v44 rss~g}!b 0 !uڦV2330`jk(#Aeٓ'O?~5kV/YDvwu)AryAAmۦO>&`YpXVa`R233M&yd2ťa8'%%s8FnqL||*fsjE1 M^1xA,Aᢻx8AWRA%&&"e… Z,fzvn( Z-`t; DAӴnQT:F4̌X=w\GG5<ϟ>}nwIII ϲh4PiB=VѨaX*udHw@A;;{kjj+J}.q.]B興{\>UFVccc*jI)))aaaq^PRR4}ԩO>A x<ʏ>Mx$V ӣ.ݎKHOO\($$$--jjj^/Yegg'''߾}***^xᅈ%557x׼^/E9O84k֬ٳg'$$0 vܹk8W\) .\ن (((55Ukr 8vd111O@R%''d4L111Y5554M߾}믿޸q#:eLV\\?vF|Q\qax\\4ӡ!+ddoڴ !ߞ>}?lʔ)8?Nl0rs'_r >}zhV\YWWWo߾ǣ8'$$l޼yŊf(LSRRRSSj rW[[efs)Ν+~>'!B& @̙3Ϟ= '...}BTTƍ;;;nwEq֭+MIIwE&  9!|>ߵk>lOCC0lƌ+V-[Ǐ#] I011%&T! kmmcYt1m41|>)rx$nɓ'wx9̌;vH-fܹVffK/B@DXXׇlĤ$ԋxRRFijjQe1J2~d2}Gf^vmZΝ/FeϜ9w>t=LOOۺuhR,EQoFeABtm۶ ӟT]}],PaĘXFvpOOOOO""dV[xц D~f-(4?uww]֭NP*˖----}f;vo4=8aY<̼yĠ ǵ]<8s%;v&RT!Vo\.eM&NOORTN:5??_0> q\R%''\˲wt‚`"~ !x{޹3$XhMX,7o޼u2d)F1 TtZN>f:˲ϟ?pבS;aX*p8?,ɦO/\zuqq,M,aX@@@ppR޳l_|ѣǺSNٺuYc&E}w{A!Y&11ǻ`W^C%q P{Ѫ>sBt%K !|FܹsV dl۶---md| N;~:;;;88$I\_yy]/v{sϽ~ꫯfo<666///+++<<\R?B+EQonnn٬0 tYY~H?8<?ʫW2  )caX{"p ̘;w…  0Lgg˗f3c@)S>rE ,kZ:::;::zzzD$I!")))333)))999!!A>0 Rtt:A/ 8h4!!!*JV@,@S%D< ߞ3YΒ"zTXtRaw profile type APP1xuRQr!9lNf:m'(6eF㉨_q.?YŌbp!J jnX[|w#P%ҔmNxK> 1qtOVڛPVM@HD{%C̏C!&Qs/$іHPzEjn.\0j37wkE4w :$*셜Q6|y?ϭDAYʻ4H4v# xܪ?7޹+X_Nwx<5jۂGX;Az0iTXtXML:com.adobe.xmp 195 195 0 ujIENDB`fotoxx-15.11.1/icons/prev_next.png0000644000175000017500000000336412616075370015517 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-15.11.1/icons/rotate-right.png0000644000175000017500000000561412616075370016116 0ustar micomicoPNG  IHDR00WbKGD pHYs  tIME 9͖ IDAThY{pU}s_I$!^K%ࣤPQEFB:08RGhQ+V1 DyK Hn}~㜛{oH3v;~~{ BiԀ01aP%`>+|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-15.11.1/icons/undo_redo.png0000644000175000017500000000743212616075370015463 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-15.11.1/icons/zoom-.png0000644000175000017500000000310712616075370014541 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-15.11.1/icons/choosemap.png0000664000175000017500000001503312616075370015461 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-15.11.1/icons/down.png0000644000175000017500000000146112616075370014450 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Ņᩏnju=^|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-15.11.1/icons/broken.png0000644000175000017500000000364012616075370014762 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-15.11.1/icons/bottom.png0000644000175000017500000000153112616075370015003 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-15.11.1/icons/help.png0000644000175000017500000001034712616075370014434 0ustar micomicoPNG  IHDR11sAsBIT|dIDAThil\uevJ$jeIe+rM:-@ --P@Ѧ A?ڠ@$S'i Ij,wDHpmw7iH9umay?s߁"E'[Vk0+BH# RG2x|S')^G )~㶴ug۶aH†DӘ.FEČq?ůpswR ]k-XLP".$(?M민DS*t$D; zc*R&N;2sHi^Q>tkцx.BS F|xCm] ւq mhfc~>4X5wo5cݯGsh۴;qdOB]v\Skk1ƒހuEZV~HW8{ s9z{0{wzwoS4  dcU_ r]e_!9"._g:6ls;q!:=-yLyXNTy@z.ҕrX;[C)Ņ|*=شqR={u/bcEWIr;@iMcܛb.\udH Mӊe_j6l\]ߧ=v.8?1ٶ@ȅ̍ax$fs_Di|`u07\dnpHsC%憊J(k9ƘE){0Ihf͞/q, @XF.ê/ #GO=|MkI w»1d*hԜBUJ'M4ݍp7n%ІR1VG|O=$?1ĩEA!:Voβ'd2 oR)K%D:/c ds̓Hi1F nT*K!A#ɰo^fg :}!%Oq։E B5$e`|c I!Ƥ$ܷo5ܼ5Ձ[4s1ƪېIqFaZ>cOXdM\v>N~*cJ79v+<Νg1q @ t$YRJl?y7Ş;zׯgiGgKJ Q\YhN2ѳ.R xұHGMu`w 7ncĪ+>6yYr97-OĜ;SYՅ\-M#g˗%]qL88@HC iBL悚\?AL9 /k4 PdHܼJ@MN! 3H"=DH1&0jF`&ŴeɺnƂր& fg!,UfR[Ivl ۳>2oMǵ Vlau L5;SMðV$*)+??@fi#0/, C: )sԂ""ŔJ.PIZ X֘8ƪ X5H )[XLc4eE  l61`4LuR+o#̖AXcөXmQIҒw>8u ܦuZ`"A*`#ql\ttcl-!Mh!Jb.L1 &$F_ J‰ׯpl&Mo1rph#۪A8K;Q PPsZt *(²ٶ׮>2^fdfDIXdQsrNs ^L!]؞Gv]7adR &΢Z;vѸg6>\ 9ye'eu,ee|w&.!oq} duW )6 B|m;h{mpMkkp;CGig}7RJw$ٱB7t"|@H"Ac֪~MKw-3/ҽ6f3͛y '9},E$X `fv;N9>o5kYV,nd醍74 Q "x3q=x|(xaN>"@+:z78rkVW+!%j)Y$Q(qY @ggͫ΍<к7o_!^|\'qC- v9-=kW/u R˸^!,O?0 =2RNFarA@F(*i%kdrdrYܬѣH++oLe2'Q,ٴv*@X sSUnˆRR JA$p4DB@RPQ:V(!.F"ǎq }e61"0pcMܹ ݟN֒o'bY}{݃ؼiauI\u|MDIQtH)])@1{*FYͦp9CG[3^E zqDRaꗴGߧ68 )Ⓒ2(?B₃K@bbRSȎa5>ZC68Οݻ +WԮqEN^kpЏ—澍9~=f __^V3/N,Y||gjQ1Sg;i {d2y.^mU+sWE]&t5ѷSX k֬_ (:| AF~J̠PsKQa"&=$ @́I&o?!Lb_.¼$NM{:ZV鈴mٚz֜x(ôo uu_srX+ (VƤ5i8K=fZ~J\pkDhxv]<ν'e.'9>CQ L.V IJ,{g&,)`x_Q.T,r{{ M4oZN]*UL[% 1*I2TjJ1`ѥȵ?<*=-ұ@{}Լ-w!oQ7$E0J n4X"1@9[X%lbvu%~=Nm]z" 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-15.11.1/icons/top.png0000644000175000017500000000145012616075370014301 0ustar micomicoPNG  IHDRw=sBIT|dIDATHkAǿof&iƴM:Mi+ *R<=T_ Oɣ7,J^/^D^DAJlM˾ݙ>ͼ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-15.11.1/icons/separator.png0000664000175000017500000000230312616075370015477 0ustar micomicoPNG  IHDR>NgbKGD 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-15.11.1/icons/goto.png0000644000175000017500000001101512616075370014445 0ustar micomicoPNG  IHDR00W ;iCCPicmxڝSwX>eVBl"#Ya@Ņ VHUĂ H(gAZU\8ܧ}zy&j9R<:OHɽH gyx~t?op.$P&W " R.TSd ly|B" I>ةآ(G$@`UR,@".Y2GvX@`B, 8C L0ҿ_pH˕͗K3w!lBa)f "#HL 8?flŢko">!N_puk[Vh]3 Z zy8@P< %b0>3o~@zq@qanvRB1n#Dž)4\,XP"MyRD!ɕ2 w ONl~Xv@~- g42y@+͗\LD*A aD@ $<B AT:18 \p` Aa!:b""aH4 Q"rBj]H#-r9\@ 2G1Qu@Ơst4]k=Kut}c1fa\E`X&cX5V5cX7va$^lGXLXC%#W 1'"O%zxb:XF&!!%^'_H$ɒN !%2I IkHH-S>iL&m O:ňL $RJ5e?2BQͩ:ZImvP/S4u%͛Cˤ-Кigih/t ݃EЗkw Hb(k{/LӗT02goUX**|:V~TUsU?y TU^V}FUP թU6RwRPQ__c FHTc!2eXBrV,kMb[Lvv/{LSCsfffqƱ9ٜJ! {--?-jf~7zھbrup@,:m:u 6Qu>cy Gm7046l18c̐ckihhI'&g5x>fob4ekVyVV׬I\,mWlPW :˶vm))Sn1 9a%m;t;|rtuvlp4éĩWggs5KvSmnz˕ҵܭm=}M.]=AXq㝧/^v^Y^O&0m[{`:>=e>>z"=#~~~;yN`k5/ >B Yroc3g,Z0&L~oL̶Gli})*2.QStqt,֬Yg񏩌;jrvgjlRlc웸xEt$ =sl3Ttcܢ˞w|/$bKGDC pHYs  tIME2;HSIDATh͚W?w.,v$Ui-HhllCS1%4mj%jAHRVB[CƢGjB4դ?(&(lV-v̼7޾I;͝9ssuj^*@d]*uJUU}XDҾE+02t~+r-95V-`u_ Z~D7jUF+i|*IH@)g25noAʨאVS`#[=$I9AqSr(X+:~Q (`|+:Uf ӍILFM2#S qӵ|hp)g^-wO׿ye#߷d!^5@ʁ߸z-a^#m'2#ӰpWLXuUbYh@B 6[kB7}ᾖ {s͟57>n_--c:NzY|ρ$-8Ā#Gƀ6VBX髊XqBo`&ڽT90 %yӠ t' sScBs da{\(R]|RȦ܋'>1vK wOn)m z5v>z[-(,:sLiOJř"!+VQSk4$9G'|s&Nkw~en1*7ZvU] Ɖؐb&S;Xiعo_7oloZ :%Dއ⹐2V|i޽ cgF rUqB @!?dsd y}Uܶvv+Ֆ!5˾V]t\}eS(ڧw9}j tl]j )q҅ap lt6}_L'WuۆAnh1=s vhix\:~Ff,D]jF@0,=9~R*[q%VH_-aİb`&rOzNn!*hb L2I]%QElH =n,e7|#3"24# 2 $ÅwD+UAD>"+b1`EGob>Sp̥_.~k-4S+a ,R.Ax`KS(S,m+duq͖x\hVH86 6 |+a}OA N]֝hl^K#u beLRK <?Owj1Q.rL؇Gs h&(5Lk&4o1)cHzxz1ww{̡sWQG0ۀ&HYoKL@JP!Jf]<JA^x{GWBLqg5݃D]yg )&G~v72J![ʰ|ѲcϟxjLEjm@5UXaxUeRg:Nz[9hsucbsJ @8Iw{<rjZD%4kV\ R֡vڟ߶Hݏe~u50הS|&=e*~"to<N}U$klDL*QJ;jΆEN5k*8bg@"|&BX^9yc4)LMdםcoRxhIENDB`fotoxx-15.11.1/icons/scroll.png0000644000175000017500000002017312616075370015000 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: LLOVm[-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-15.11.1/icons/edit-funcs/0000755000175000017500000000000012616075370015032 5ustar micomicofotoxx-15.11.1/icons/edit-funcs/invert.png0000644000175000017500000000116012616075370017045 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-15.11.1/icons/edit-funcs/shiftcolors.png0000644000175000017500000000161112616075370020076 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-15.11.1/icons/edit-funcs/stacknoise.png0000644000175000017500000000572412616075370017713 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-15.11.1/icons/edit-funcs/expand bright.png0000644000175000017500000000207112616075370020257 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-15.11.1/icons/edit-funcs/panorama.png0000644000175000017500000000105012616075370017332 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-15.11.1/icons/edit-funcs/blur.png0000644000175000017500000000441512616075370016510 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-15.11.1/icons/edit-funcs/tiles.png0000644000175000017500000000647112616075370016670 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-15.11.1/icons/edit-funcs/warp affine.png0000644000175000017500000000431212616075370017722 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-15.11.1/icons/edit-funcs/crop rotate.png0000644000175000017500000000212312616075370017760 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-15.11.1/icons/edit-funcs/keystone.png0000644000175000017500000000351412616075370017404 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-15.11.1/icons/edit-funcs/embossing.png0000644000175000017500000000303612616075370017530 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-15.11.1/icons/edit-funcs/pencil.png0000644000175000017500000000240412616075370017012 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-15.11.1/icons/edit-funcs/RGB.png0000644000175000017500000000240112616075370016147 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-15.11.1/icons/edit-funcs/smarterase.png0000644000175000017500000000156712616075370017717 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-15.11.1/icons/edit-funcs/vert panorama.png0000644000175000017500000000123212616075370020275 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-15.11.1/icons/edit-funcs/HDR.png0000644000175000017500000000101512616075370016152 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-15.11.1/icons/edit-funcs/CMYK.png0000644000175000017500000000302612616075370016304 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-15.11.1/icons/edit-funcs/dots.png0000644000175000017500000001031712616075370016513 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-15.11.1/icons/edit-funcs/resize.png0000644000175000017500000000174312616075370017046 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-15.11.1/icons/edit-funcs/brigth ramp.png0000644000175000017500000000532112616075370017740 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-15.11.1/icons/edit-funcs/bright distrib.png0000644000175000017500000000135012616075370020437 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-15.11.1/icons/edit-funcs/flip.png0000644000175000017500000000162312616075370016474 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-15.11.1/icons/edit-funcs/fix stuck pixels.png0000644000175000017500000000246112616075370020730 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-15.11.1/icons/edit-funcs/text.png0000644000175000017500000000374612616075370016536 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-15.11.1/icons/edit-funcs/fix fringes.png0000644000175000017500000000046112616075370017745 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-15.11.1/icons/edit-funcs/leverage edit.png0000644000175000017500000000335312616075370020244 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-15.11.1/icons/edit-funcs/paint edits.png0000644000175000017500000000445112616075370017750 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-15.11.1/icons/edit-funcs/stackpaint.png0000644000175000017500000000223112616075370017677 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-15.11.1/icons/edit-funcs/brigth color sat.png0000644000175000017500000000330212616075370020664 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-15.11.1/icons/edit-funcs/warp curved.png0000644000175000017500000000335512616075370017770 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-15.11.1/icons/edit-funcs/landscape.png0000644000175000017500000000160712616075370017476 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-15.11.1/icons/edit-funcs/paint pixels.png0000644000175000017500000000473312616075370020147 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-15.11.1/icons/edit-funcs/HDF.png0000644000175000017500000000332212616075370016141 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-15.11.1/icons/edit-funcs/unbend.png0000644000175000017500000000350712616075370017020 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-15.11.1/icons/edit-funcs/whitebalance.png0000644000175000017500000000342612616075370020173 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-15.11.1/icons/edit-funcs/tonemapping.png0000644000175000017500000000757312616075370020075 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-15.11.1/icons/edit-funcs/sharpen.png0000644000175000017500000000367712616075370017215 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-15.11.1/icons/edit-funcs/warp area.png0000644000175000017500000000245312616075370017406 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-15.11.1/icons/edit-funcs/voodoo.png0000644000175000017500000000620512616075370017050 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-15.11.1/icons/edit-funcs/warp linear.png0000644000175000017500000000302612616075370017745 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-15.11.1/icons/file.png0000644000175000017500000002422712616075370014425 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-15.11.1/icons/combine.png0000644000175000017500000002257112616075370015122 0ustar micomicoPNG  IHDR@@iq zTXtRaw profile type exifxڝY#9DsA8\}yUMDBpֳ喾y _~}Fۋ>ſHއr{K.^'DkU[}lH*Ev{J.Z:G3{{7 RT{gidu|%?^re[a+_&_Y~~ݡ] a_}To H_+ǻ4vl;sN' =ޭd37ssϋHޢ<9%mw77)/Ӽ_opYa&]_2 0uZ `9bO)Ti3)N;VФ@HGj^ːuSJW͢N4߇R(-:X&Tb~@.^`OE mlF )*aڈ .ݚd A>LҙʳACQ:_q8Bs=y>npSQSƯ8=TJJKSK)x&}wՠ48Zfr&>7P\!@ėgR V¶SFh-dv^Ȗ, ܲrEeӻ!^Mn;[gXuvf1YO@;X6ptSa`uR~dSѓʨ'*i A^,*u^jM+ua{mY:PEAAkexUYeET]Vh9ocprPnf*\cla%3_ p`L.ȶFyG=m2P]mȌ*q*浜\ndin1v"{;. XԴ=v%*S ziб. nvLz ~-ʅ !ؘ'Z~<dDZ]ꁩTI>hϠ$#y#*`@\Eێi:qzd5㦅Ծc 4t@Zá&t07FRy A~Gd hyBH8Љ!S}RE2:9uAca_Σa w&=gk'㺳:kc(w7KX77֬Q;O bmOKix]0^uhFcL s0WSTVu,|!aYMӾpU1 `.w 6IoܠTc*9_XKVLx36a+\}Z:؋l{hTF >0rH" X.k!h-\-oŰB:d$M @ yd=O.fBd) F4lcZHjiD4\/+8):mN,ҁ !4*ބUtYJ#O{yDjSb6m5wMFA0dyhH1Hb4hr${XWx`k| lh#t2Б\Od#f:35OGI܀(F xYh؃ qqpF ]!$=YvCqV+pr#_]72c\d`9iT "lKÀo:R<#usBIT|dIDATx[tTם{SN]T*ݯ!! EfncBݬī'q'IIK?efe9=+Ig%LƓv6W BHBw ݐJRթsΞ@5v09so{%_ZK>l@Y Oي?)JHb˯ѓs-v얊Z{vOѦϜ}V\|==\I2WX";1)zFR&+==5qWiKjKB}VLSd-SxJbr&娕q{IS{/cQ ٲk(ty!F 3mm e|mh>@K/BlM(UND,my7 Cߤ٣Xn)L$=C4PICG/SӠHMW $u3غGj(-qnRtK8i5=BcDf낌ޕ6yqɚL$;éISW?# `>{0M@'HO$pQЅ$7ƺ%%j{MJ &5Ҳm Nw,sJ3f"ױBc7..u7*bc='axMzvDDƈǧj%RA?5 ԶUj}ei-ۚz>9 wnX8cfw?W[;M"qAK0iA BPo"$c4:e%pS=_RR% yf19 W&^GRH3I'K7ncXҞ&uxaz}H,;xx2wJMiMpOg"5 JFpWR|ԖkuN}\KQPMy4Fw/^?Oؽ=~sj<t\JDIb xv\\9=27?R">>_N6hѱb(m*kػol]IQv5]rYydY'G/4շy}/bBUesr.g\ f{S̑S: 0 |EK4:m[W|A{֔29:Cطo<%+-̀Y3</rmzG'%lD+`Ogd!MRpas ;|0t$3PLj%% ybb-[@^M_$&_3B99Dž} dhJ,ⰗuE&[]D4:+ Qnh?"TYL$aVq#1|t=O}~1KWyM#9:_O24 ]jHMG \/B"E 評4ԧ@{\pQ \Ժ*xr*]v\ຳBJAE)`ǒ3-N1سuey*3BPII2JO^mZgZ]ͮkEOLA/m0'oWhM9rWu*㟾PˣՅ4Gxx+Go M8@#͋2|gyhqZ/{h#,JIYZǎL2}(!xB4z ӀA_kcG:OLLFhVg'}6-_<_A8?>|DlfpmK0=/\;k ;fgE>Kq:F9pStLb%ض4tH)<.Igom!; O>2][',? v񸘵"ꛒdޑsyq:fz}SӃ4>36H&P$~pG8Gqϊ|?+) )#`*W?)Mǡg0ӻ:MeA޻I}kCIo\m[@ptX7"A% $)o`y^zR7=~;3ۧ][X[%tk(Cs7%2 Z'YC8-kݼN$;|."/DY>1VzZ{w ?E׵0:|`}PҖӃخK޾[ mߝ}"hL۫2*ÎiP(ۓc)XL TYq#hdr߅O޸LSh=ݐl+f17x晁wm9ۗ/ +ץuySYF䓜*N)O֚vu@G8t5}f6]RG$ř--fCYWiqCQl^7hrRbAZXm?;vG:7LΒt]5,-toP _[~Ïv#`*wAM3dbw?:T)CB  V/5-(+h8jۼ0?6>R\{%lf.~V׫MUd4(AalE}Zӡ2*<k>wgz1=jm!LU/U1lKxLrRl泊OHIiEM\Cc(gM9;k(74t Lr1 #L|&LGGwIē[ƼAYi{\8l(3'):waqi`OR9NR29ct?l18U;ޯSڟ=_TA<)IENDB`fotoxx-15.11.1/icons/rotate-180.png0000664000175000017500000001352312616075370015311 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-15.11.1/icons/down+.png0000644000175000017500000000154612616075370014527 0ustar micomicoPNG  IHDRw=sRGBbKGD7A pHYs  tIME psIDATHǽkP7L;fcbX\(p_* QpBQ0[AgZ+mmk瑙$܌Iuy}7|b͝%De2V!$`+ H" Zх@haY PR:@y ΈSW_ah^@sg` 8,~W9cR /5[-+DZdÌ+RGQr蚈 #"7 +Xކ>81S9@9FNYWk/"A}e)%& kqًgl&] ;!Ih*-#e::ڽ}ȏ9,Gh8kimkf&W ]>oMK5%jͤM>~f$+8$dKO&NsyxJ@M?ωDݭ۽ 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`}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-15.11.1/icons/tools.png0000644000175000017500000001505112616075370014641 0ustar micomicoPNG  IHDR00WzTXtRaw profile type exifxڥ] =  p2 ?srOos}; y{?ލZȿyۿ^ȕ,j6z_,ZP*mtmJI] ó;pR1sM\dƽ0VPԯ82\'-?>x{Q?'M^r3HaXqh`~|c1_|u(+qոsbY7ĥEo׋p[+ϝgKI_!!&Ʌ~ߣW|KWp'Κ{)EJɻ6c/[׾ a1s>7B Q*P?Ǯrr%[F R0Rf4}֒?ᐯH+_4=$d)2+ZGyc},o6/Vv|敻iz^Nqgi"}zؕk6LxAL5G3ح>T؎=y]=93u#/,a߹BrVkø4f2-X}B)yztk(f֗WlE/]ʰOr7l;o~_Gn kwA@w3IǙ=QJQjvXaksë!)d8͊%A րJK0"s!s0E\);P|t.kkIx!1i8TLM.]܋m޵-8K&V[*aډ L~"O,7^I #R,/gA$FYYÜ G8R:{ 鴄\uc>wΎK&pL؀+"w+3ٯPlW;4ed|Fg Tc>55g el&s;M~r@:Ldzl{2@WDDWgTQmn)j[% ȩɋ&AVPN-$,<\ҝ5/4!'yӗ#LSѼLDUK)6TLs9`YJ]KJ|"}%KA~DR,m! IN745l0,CdN,۴Q+;=PUKGǦ --U 5*˼m*0@RrPZZHJWx÷yW5MNc@ ~SO4)M <1١x90B@c^9,]%pS^-衒~"g__jyrd>o&D7붙]aFsN((kk"4wxWEi8}۵![Ջ7}.6-"WL|핉S?.j_1* ,Ν"^B&'iIY@KsɩY]52CM9;ҠgûsBIT|dHIDAThZytTU[kIU*! DPFU[Ew3vQTZq F`72$!{*ϙw=z}ozK+NmHDuex$I톢(̚5wgY@(((vc͚5aA.=%[;W֭\+Xrrr}s:fqqyG_nW\yOu]25Mf"3g4o޼NϿuݺuڵkNmiӧ|XU]YW^|ŕ6m ݻ7ku(AX`&,YBt';]w_}?3->wX=[O[c'Yhʍ7&컭3{ . Y&ݝI)e3UUL==lփ3._rw0{ܹsO!QX3h㦚__sp·7l9 c=^QDX$a20T]>TZ\^]];w$-_Ɂ}tw.ŋݼys4髪4Mci8׵k+Yպ)MxFiUW?؜8Vf1]{ҥKWO)`, NÌ10 ԄP(@ Bn7n7JJJr{n<p)nFG8admRJ~I#qQ.Ix GИ>l ssh!xR<(JD"%u)ƎU`a׻y\ 04%c,By9Jq<,^ eysǏʖ3<ҮMu,&{"F :J&ws(10JRPFSy0<9E1J@8˼%:%۾ FXKpEL-!vH#zwmqev{L a2MP+qBdYNI|:ǝJ h鄪/ ";5K%7_#N&[9p@j@h :R{2rҮ(0L ̊j0x(Jn8C4aQ a  `[7C1x8 !;ꎁ)<fA hh ٱOf 7'\zŲ DmDv-Z|4rcXS D"׫DR"1BB ,\9H2PS e:M8t6e2)~-!Pu,GO<nȣ0@͘i$I,ˉV$:tՅ%Ibi`ӶF3,]w?0s+~;}yLEWT7B vDtwLt£X +HtP^t?IMM0 eZ(@}>VZյt%I kii{Z(yz㇙,}T%qd֫~OF/\ѯ_vh\3ŹG /aZ0T4$ax<E1ἢ(6\UU]Z{?[[ 2UThޣD?ÑdH7jB+h 8耎;Ts L7`*n|<(cHKK$I,+ d"@cm??+##DF)|a  5&? A-#ǁ15݋8xPYYk"&=+6q2隋hEyyΐѫBI9d+"]wE?~IHբ 2RPӂJ ֩KmDQ 8~8rssQPPBhݺuϟT,ؾx&LHolU^~ϻ@[ϲ\Wetn6$lxw%K832!1A$I|/Ԏeii믿9l0>СCΟ?3y:\Xm~tP3}ޝ1֭N1WKGBup1[{ݵFif,e l˖-w:w獾nZWWe޼yj|t=9 s꤀5ptj~fGt ʬ{N |Yoƞ#FqEb{rHlرcpeMMM[c qdYڹsgͲe˖zJ,Zֳ`&T *t\Ѡ5M^Hc鶣*ohi.h2EE0,MWB's`c$Zg!c%`?] gΝflLD7CLD8; |[$}rIDO80$[) gwz>IENDB`fotoxx-15.11.1/icons/viewW.png0000644000175000017500000002134712616075370014607 0ustar micomicoPNG  IHDR@@iqzTXtRaw profile type exifxڝi 97q.UA{kf*Y$X~}*iM]5j޼ N,}^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-15.11.1/icons/areas.png0000664000175000017500000000122612616075370014575 0ustar micomicoPNG  IHDR,,ZbKGD pHYs  tIME;+ F#IDATXY1 ԥ](k{Qnеl/?"68U@Klc@rԾ 0^30$HӲn:0{~ `eqto@vOIԤ@?}R !4x܋._O wp.5M,R]FaZb7o CWQ k`is/c4 ^Z)`CaLD %OA6a.כ2US33+~%0H-`1LYYyiܐۢaYSgvb1a s(*`v%A&-&&rʢ%jcG:KI9Q/p]V[t̽5 ZF;nۚ6e{JcHX1 ƚ"2ZRnݻoH+uIENDB`fotoxx-15.11.1/icons/viewF-check.png0000664000175000017500000003355212616075370015644 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-15.11.1/icons/blueball.png0000664000175000017500000001336312616075370015271 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-15.11.1/icons/viewG.png0000644000175000017500000002741012616075370014564 0ustar micomicoPNG  IHDRPPbKGD pHYs  tIME - A IDATxIdkz{3Ęcz`7fb A[2Ӕ^yc/10+æAX j"LJINUuk)2"N8Q۟}BfUedNz~q/_\~q뗯*UEiՒ 0&UĖ%EL)E$Q@-L _1|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-15.11.1/icons/fotoxx.png0000644000175000017500000002332712616075370015035 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-15.11.1/icons/viewW-check.png0000664000175000017500000002213612616075370015661 0ustar micomicoPNG  IHDR@@% _IDAThC՚ypיؿ{{ƍ\oR$uIJ^ٻ*k%QqRMUJIYeS*)Xk{}Q+J)Dśc >1 8$}l @{MRzw I]91DDDJ 2Zڳs~C9(45vEbjHx]nu0ˮ|)_t1F p@q\}~mĖ_&8E;bH$Hk==';>{>@DWK P^kbf)&r C:z3;8PHN;T*' ;{Or3o]}c6(=&V?w5~;~_ZW/_TjPg>SІm[G:Rxp9_],ԏ82B(lhߣH{SID{gkP$ٌqn;uD1%2D*u57O0tt˱~SJFo؞CRq/ lbQMɤs?Jsw 8F(\2/};ߪ 6$4mPs0tʅ^6 ex^X?~G@@8gܦ\ʘ6/HvR}Q ƎCɶL?B}9'iXI\yțw;7 !"~{:V*$ k%uxWDd񬲑z"ąҥO,Xtdzjtj55ԯmx"o(}O~,ϾϿYOLLh"E34p93陗R kkg~.KqUq$Ok QXcWڻ:vDOOw H&i ?:k8I>#W7vEcs o|0L˘lTU#.iהnJ$BU9"!mX$+c_$Ѡ EmrnoUtMyo=`>IܳsŹ53.ӭ_狗<q/S.Ud@A-' bMViEu _ oJ[8pDD(+c~+rcr92}?wsAL$ З޵5ʼ-O!"~a+y+2%J(GJzZJO.M uGvH=8+ XޞNERfLe޷]+K<)O*ntz)755GQO{'zj_Z`Q=?9Ys B˿2`9miƵ1h[hM<*K'M" z;:sBD!D9rUWg_,T)R۷fj>:ʀUARkkrik'D?b1M_)T-doPݔHA3~b,V̒)w?qd$HæqL沧9=T]lmP~fWy`|FEZ`p()}X`%?z;P6vG^y4󢯗y]GD dqzm > D(ǹ,]{ۮEt᱅҂e\42>=*K4:5- t rh+b;F|E=@% tWsJ_XD$Jt>5W4C~o;uF>A m죾^ʟl@PQnd2A<|H@ }C];B~i13vS@p\;STDHēŗΝΔf{ >Q*xg8pCj 񡗍t8^=x5sJcHLb=.Nill*{=PٹQ=jߘB0b(ö̢D&0ꙫ?+UG2WZ֍VC|exk^}yuSr5rTGB';zqrC?t\ebxgg[mJ{<%x\ExYw$pOl+؊8=ps? +"q=  oZY)~c+/ fYԃ Ln\sIh4A"ǺQv\ 9dDO(2=p F-qb`|O0f#^@C~@V%Rns}?q/M5#?PU觝'>t]raVʕ+={Eٸl@XfȊ_3,NHGIoe@3ҔQ5( t=Y{7Vi߀z?"N<`d_b$+/]@USQmL J>qSKSݩ[q,o}k/־J~ɱ--V}! .(%Rh2ZcGd=H@uhogC6Gdv}<:Uate$?sxNc0kS 4A8[eAJ/j!ǘ:39Qt%a*N$+6c3ķ!o@tyVe4:>:+no9Nʩ/Uc8idCڐ(PGL$6RJ%IjyQXh$r3j/{ywa+Z {SyLA7qdvmcww,*_SdpϦYPX'u3\sf) A r6<[@!(pޚwoLg*/cV {\-rTE_Xu刞Y}'Q!uPX|!߰oA8gmwiD 7F7-K*DqTnu̵ [];*KnVirG|GJ*81[l0wPOF ^eêڮ@nSJµeRJ!H@gܨz0KN_3LkW}:2I$$]kcuM[h?{@~g[];5) P! !{,sW¶lg%0JПњS/EPW_M}= Ť1ƭQa֣wm{*5c3ݳ?GDݛM2{:%A5p TR>뙓z 邻]P/w^o^;1~j]mg_lvc"13ٶu~=J/b.iGH?Dcu\ oПg4!_26IM6z7T'>N ㌵ފ|qR+'r}.Ab뺭7/x W_y3..=VYkMhdZ"jږeV֫zj׿վC+]q},?)~^Q% 7G# J$HD{D>aM8@ZQ1/Ꮟ=3y^ZU`sJ^  2?z͓nZ1UCK5q+t2]?gSQ矜zs{Zq#/ݺaƞ! h7@+xjԢ.5M.dG[H-;b32D}], S.%I7c<W>xčYl[z`8rخScw̌LTNG?JA,]lƊ !ev՞-~yv߽ț׍J<- ql:F^O}o_QrP.7wF.3ІpƑqlbb)V— M*nRG'7 HP) X[,ȓo:"9>C;}R<U0O3qO07Mgml~*n6_ۖ]dYHhv;,RW@f q߷XqBWBfsm6CZ^ҞI&6ly)?,-~=wKKBV[ [ȡwG'@hJT z@$LqcPC겵])p'NeY|- nݝ7ƞBb[AJ,n+r tBn{<ھu5Ekj!e|;*}3yͱ1ذP V9ܿSjm4rdzQW<۪S>ؒ"fAPC/ @DWVu cca8j$h*,e=ƓW9kxRGZyBв¼QfMʹR12y::#GUfwGD07ot5pu.c#1ym/r\VEU&"a Wl^.[ZlL+-v ɩR^7m->8Xx婣~I C_L*͇.+ " jQJEّrdgj.^㾶πdlek3EYJ֝$~ g%Vx&tSыepXu:E$" {lk<)Xd:3ʇ,٭T;5YT,fQUgY']mp7%Kŏ$v|k=KӞѺE-??ZN~•HN=-|xW9Klm!h$+hlka"t3sPD7 w6]J I-̻ %k&Xݷ֭#Nv'3lPK?Sp`2QJ{˅ӣͻ<9,ȩ̣y{6|hjKۛMʈg?J*7F_)1]:pvLb;HZmޜRZϴ|:pڲʆQy={z:ڙ8`ZB8y̳1 J鷶Zs7GY9Ç"NYiU22ٞAV5 l0;i-dJPNV-c,teGcݫsdI%ȀR_>*#w?3/qp)Adin3DW&Ç!i;vY%(N+9O>5[1sy4m fb{+vKpF-5ST>&,'H[*&!\=4#{Ym \Lc(޹E\/}#sFM1Ƽ&@޳vtzq|4t,mxb" ~)F ﴀT@7MF,0bK>&Tqw&K)wZUIE xZß~tDFYԁNU 4MkZY-6X,27 pUf4lt+e+dMq(FenЕ"p|y6)Fbƨb3]^씤Rʜ O ',vjz(X~9(ْmmh5V6֥x.$ry|]r0Z(x'z:d5&BS&KtF25 kmӅvjI!4];A`Ao,d÷|\]2u`!9 T'%Jlni*Jx0͚!/R8!-pMƈ&^y,k, tuQ[bctA嗉f. U@JC`FfG؅yF}t 9^y0*B}Wz1Vvb2:_r*SFZ=T|B c(e_yC'4g0dPԔ冮Ug1G"Ldf5` 3b#DVfS ˺\,^!Xȗv iAv"gNR2 DИdrOQC Ѡ۰g$I;h&h9H4EqW-ZGB4KGkuۄ`;{?}c&kmiTXtXML:com.adobe.xmp 1 2 3 0 6 64 64 1 0 dIENDB`fotoxx-15.11.1/icons/plugins.png0000644000175000017500000000161412616075370015162 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".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-15.11.1/icons/search.png0000664000175000017500000000546612616075370014761 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-15.11.1/icons/edit.png0000644000175000017500000002007212616075370014425 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-15.11.1/icons/warning.png0000644000175000017500000001166012616075370015150 0ustar micomicoPNG  IHDR@@iqzTXtRaw profile type exifxڝk# *Dq9@vSۓ;3\q>bU~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-15.11.1/data/0000755000175000017500000000000012616075370012567 5ustar micomicofotoxx-15.11.1/data/tags_defined0000644000175000017500000000020512616075370015123 0ustar micomicopeople: Mary, John, relatives, places: Yellowstone, NY City, things: monument, building, garden, events: Danube cruise, 2012 Italy, fotoxx-15.11.1/data/edit-menus-es0000644000175000017500000001004412616075370015170 0ustar micomico Imagen =========== Girar 90º Girar una imagen a derecha o izquierda 90º. Recortar/Girar Recortar los márgenes no deseados y/o girar una imagen Recorte automático Recortar los márgenes sobrantes de una imagen. panorámica, deformada, etc. Redimensionar Escalar una imagen a mayor o menor. Voltear Reflejar una imagen horizontal o verticalmente. Añadir texto Escribir texto en una imagen (fuente, tamaño, transparencia). Retocar ====== Mejora automática Mejora automática con un clic. Retocar luz y color Editar brillo, contraste, color, saturación. Mapeo de tonos Incrementar el contraste local para mejorar detalles. Histograma Aplanar o expandir el histograma Rampa de brillo Variación del brillo vertical/horizontalmente Editar pintando Pintar una edición de retoque poco a poco con el ratón. Editar por niveles Controlar una función de retoque usando brillo o color. Reparar ======= Enfocar enfocar una imagen borrosa Desenfocar desenfocar una imagen (p.e. suavizar piel) Reducir ruido reducir ruido (motas) en imágenes tomadas con poca luz Borrado inteligente borrar cables eléctricos y otros elementos Ojos rojos elimina ojos rojos de las fotos tomadas con flash Pintar/clonar pintar con el ratón usando un color o un àrea de la imagen Eliminar polvo elimina manchas en imágenes procedentes de diapositivas sucias Suavizar bordes eliminar pixelado (dientes de sierra) en imágenes de baja resolución. Corregir franjas de color reduce la aberración cromática que causa franjas de color Corregir píxeles retenidos corrige los píxeles brillantes/oscuros producudos por defectos en el sensor de la cámara Color ===== Cambiar colores Gradualmente cambiar colores RGB en otros colores Modo de color Cambiar a blaco y negro, color, negativo, positivo, sepia. Perfil de color Convertir el perfil de color (p.e. sRGB <> Adobe RGB Concordancia de colores Hacer coincidir los colores de una imagen con los de otra. Revisar RGB Hacer correcciones de color que varían por toda de la imagen. CMYK Cambiar el brillo del color usando colores CMYK o RGB. Deformar ======== Enderezar Solucionar problemas de perspectiva (esp. Panoramas). Corregir perspectiva Hacer rectangular un objeto fotografiado desde un angulo. Aplanar pagina impresa Aplanar/enderezar una página de libro fotografiada Deformar área Distorsión elástica de la imagen arrastrando con el ratón. Deformación en curva Distorsión curvada de toda la imagen arrastrando con el ratón. Deformación lineal Distorsión lineal de toda la imagen arrastrando con el ratón. Deformación afín Distorsión afín de toda la imagen arrastrando con el ratón. Efectos ====== Profundidad de color Reducir el número de colores (también conocido como "Posterizar"). Esbozo Convertir a un dibujo simulado a lápiz o tiza. Dibujo con líneas Convertir a un dibujo simulado de líneas coloreadas. Dibujo con colores Convertir a un dibujo a color simulado Desenfoque graduado Desenfoque graduado dependiendo del contraste. Relieve Convertir a un relieve simulado (efecto 3D). Baldosas Convertir a baldosas cuadradas. Puntos Convertir a puntos (efecto Roy Lichtenstein). Pintura Convertir foto a una pintura simulada. Viñeteado Cambiar el brillo o color con un patrón radial. Textura Añade textura a una imagen o área seleccionada Patrón Añade un patrón como fondo de una imagen Mosaico Crea un mosaido usando baldosas de todas las imágenes disponibles Matriz de convolución Editar y aplicar una matriz de convolución personalizada a una imagen Combinar ======== HDR alto rango dinámico Hacer una imagen con un rango de brillo ampliado. HDF alta profundidad del campo Hacer una imagen con gran profundidad de foco. Apilar/pintar Quitar objetos transeúntes (coches, turistas). Apilar/ruido Reduce el ruido al promediar varias imágenes. Panorama Unir horizontalmente una serie de imágenes. Panorama vertical Unir verticalmente una serie de imágenes. Fotomontaje Unir múltiples imágenes y texto formateado en una sola capa. fotoxx-15.11.1/data/quickstart-ca.html0000644000175000017500000002462512616075370016241 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-15.11.1/data/quickstart-en.html0000644000175000017500000002046112616075370016252 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 - Runs after initial installation (slow) and afterwards as needed (fast).
    Creates an index of key image data. 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.
    World Maps: View a map (world, continent, country, city), click on a marked location to view a gallery of photos from that location. Requires optional package fotoxx-maps.

    Menus

    File: Select an image file as the current image 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 image captions, comments, ratings, tags (keywords), geotags (locations) ...
    Search images using these criteria, file and directory names, dates, or any image data.

    Areas: Select image areas to edit separately. Copy and paste selections 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 ...

    Bend: fix perspective, straighten curves, flatten book page, various types of warping ...

    Effects: posterize, convert to drawing or painting, emboss, tiles, dots, cartoon, vignette ...

    Combine: combine multiple images for HDR, HDF, stack, panorama, mashup.
    Undo / Redo: Left / right click for before / after view, within an edit or for each completed edit for the current image file.
    Tools: User settings, batch functions (move, rename, convert, RAW files ...), manage albums, slide show, various utilities. Used in both File View and Gallery View.

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

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

    fotoxx-15.11.1/data/custom_kernel/0000755000175000017500000000000012616075370015441 5ustar micomicofotoxx-15.11.1/data/custom_kernel/denoise30000644000175000017500000000024512616075370017076 0ustar micomicokernsize == 3 divisor == 16 cell0101 == 1 cell0201 == 2 cell0301 == 1 cell0102 == 2 cell0202 == 4 cell0302 == 2 cell0103 == 1 cell0203 == 2 cell0303 == 1 fotoxx-15.11.1/data/custom_kernel/emboss50000644000175000017500000000063012616075370016740 0ustar micomicokernsize == 5 divisor == 0 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 fotoxx-15.11.1/data/custom_kernel/blur50000644000175000017500000000062512616075370016420 0ustar micomicokernsize == 5 divisor == 57 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 fotoxx-15.11.1/data/custom_kernel/sharpen30000644000175000017500000000025512616075370017111 0ustar micomicokernsize == 3 divisor == 3 cell0101 == -1 cell0201 == -1 cell0301 == -1 cell0102 == -1 cell0202 == 11 cell0302 == -1 cell0103 == -1 cell0203 == -1 cell0303 == -1 fotoxx-15.11.1/data/custom_kernel/emboss30000644000175000017500000000025212616075370016736 0ustar micomicokernsize == 3 divisor == 1 cell0101 == +2 cell0201 == +1 cell0301 == 0 cell0102 == +1 cell0202 == 1 cell0302 == -1 cell0103 == 0 cell0203 == -1 cell0303 == -2 fotoxx-15.11.1/data/custom_kernel/sharpen50000644000175000017500000000065712616075370017121 0ustar micomicokernsize == 5 divisor == 16 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 fotoxx-15.11.1/data/custom_kernel/outlines50000644000175000017500000000065612616075370017322 0ustar micomicokernsize == 5 divisor == 0 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 fotoxx-15.11.1/data/metadata_short_list0000664000175000017500000000062012616075370016544 0ustar micomicoMake Model Date Time Original Rotation Orientation Flash Focal Length Focal Length In 35mm Format Color Space Shooting Mode Exposure Time F Number Aperture Metering Mode Exposure Compensation ISO White Balance ICC Profile Focus Mode Light Source Keywords Rating Caption-Abstract Comment User Comment Description City State Country Location GPS Latitude GPS Longitude Creator Copyright Image History fotoxx-15.11.1/data/slideshow-tone.oga0000644000175000017500000004157212616075370016234 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瘞.랻ի Adobe RGB Concordància de colors Fer coincidir els colors d'una imatge amb els d'una altra. Revisar RGB Fer correccions de color que varien per tota de la imatge. CMYK Canviar la brillantor o colors usant colors CMYK o RGB. Deformar ======== Redreçar Corretgir problemes de perspectiva (esp. Panorames). Corregir perspectiva Fer rectangular un objecte fotografiat des d'un angle. Aplanar pàgina impresa Aplanar/adressar una pàgina de llibre fotografiada Deformar àrea Distorsió elàstica de la imatge arrossegant amb el ratolí. Deformació corba Distorsió corbada de tota la imatge arrossegant amb el ratolí. Deformació lineal Distorsió lineal de tota la imatge arrossegant amb el ratolí. Deformació afí Distorsió afí de tota la imatge arrossegant amb el ratolí. Efectes ================= Profunditat de color Reduir el nombre de colors (també conegut com "Posteritzar"). Esbós Convertir a un dibuix simulat a llapis o guix. Dibuix amb línees Convertir a un dibuix de línies acolorides. Dibuix amb colors Convertir a un dibuix a colors plans Desenfocament gradual Desenfocament gradual depenenent del costrast Relleu Convertir a un relleu simulat (efecte 3D). Rajoles Convertir a rajoles quadrades. Punts Convertir a punts (efecte Roy Lichtenstein). Pintura Convertir a una pintura simulada. Vinyetat Canviar la brillantor o color amb un patró radial. Textura Afegir textura a una imatge o àrea seleccionada. Patró Afegir un patró com a fons d'una imatge Mosaïc Crear un mosaïc emprant rajoles de totes les imatges disponibles Matriu de convolució Editar y aplicar una matriu de convolució a una imatge. Combinar ======== HDR alt rang dinàmic Fer una imatge amb un rang de brillantor ampliat. HDF alta profunditat de camp Fer una imatge amb gran profunditat de focus. Apilar / pintar Treure objectes transeünts (cotxes, turistes). Apilar / soroll Redueix el soroll en fer la mitjana de diverses imatges. Panorama Unir horitzontalment una sèrie d'imatges. Panorama vertical Unir verticalmanet una sèrie d'imatges. Muntatge Unir múltiples imatges i text formatejat en una sola capa. fotoxx-15.11.1/data/KB-shortcuts-en0000644000175000017500000000047312616075370015446 0ustar micomicoN Rename Image File K Keyboard Shortcuts Alt+G Grid Lines T Trim/Rotate V Voodoo Enhance R Retouch Combo U Undo Shift+U Redo P Open Previous File fotoxx-15.11.1/data/KB-shortcuts-ca0000644000175000017500000000032012616075370015416 0ustar micomicoN Reanomenar imatge K Dreceres de teclat Alt+G Línies de graella T Retallar imatge V Millora automàtica R Retocar llum i color U Desfer Shift+U Refer P Obrir arxiu anterior fotoxx-15.11.1/data/userguide-en.html0000664000175000017500000164533112616075370016070 0ustar micomico Fotoxx User Guide Fotoxx User Guide  v.15.11
      
      
    Fotoxx Overview description, prerequisites, license, downloads, capabilities
    Fotoxx Usage
    initialization, navigation, menus, general procedures
    File Menu open, save, rename, trash, delete, print
    Metadata Menu edit tags, geotags, ratings, captions ... search images
    Area Menu select image objects or areas for separate editing, copy, paste
    Edit Menu trim/crop, rotate, retouch, color, tone mapping, add text ...
    Repair Menu sharpen, blur, denoise, red-eye, paint, clone ...
    Bend Menu bend/warp, straighten, fix perspective, flatten book page
    Effects Menu make drawing, painting, embossing, cartoon, add arty effects
    Combine Menu HDR, HDF, stack, panorama, mashup (montage)
    Tools Menu image index, user options, batch functions, utilities
    Albums Menu create and edit named collections, slide show
    Batch Menu
    batch tools for file conversion, metadata, RAW import ...
    Help Menu quick start, user guide, translations guide, change log ...
    Organizing Images optimize for searching and viewing
    Translations
    instructions for translating the user interface language
    Recent Changes recent functional and user guide changes
    Technical Notes technical methods and limitations
       

    Fotoxx Overview

    Description
    Fotoxx is a Linux application for editing photos and managing a large image collection. The goal of Fotoxx is to meet the needs of serious photographers while remaining fast and easy to use. Fotoxx is standards compliant and does nothing to compromise use of other photo apps. Fotoxx has a rich set of editing, repair, and special effects functions. Image adjustments are displayed instandly in a full-size image, allowing interactive optimization. Fotoxx has a rich set of functions to organize and index a large image collection so that finding desired images is easy and fast.
     
    Hardware Requirements
    Fotoxx works best on a strong PC: 3 GHz multi-core CPU, 8 GB RAM. Multiple processor cores are used for compute intensive functions. A weaker PC will generally work, but may be slow for some functions and unable to edit large images. A monitor smaller than HD (1920 x 1080) may feel confining. The monitor should have good color fidelity and good coverage of the standard sRGB color space.
     
    Software Requirements
    Most recent releases of popular Linux distributions will work (Debian, Ubuntu, Fedora, Suse, Arch ...). This should be 64-bit Linux. 32-bit will work but will not be able to edit vary large images. 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 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 origniates from the author's web site: kornelix.com.
    If you have questions, suggestions, or a bug to report, you may contact me.
     
    Downloads
    Latest source code for use with $ make:  tarballs
    Latest installable package (.deb):  packages
    Fotoxx packages are available on many web sites and Linux distros. Some of these are quite old and should be avoided. It is better to use the above links.

    Optional Package fotoxx-maps
    This is a set of geographic maps covering the world. They display image locations as 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 manually. You can add maps of your own at any scale, and your images will populate the map automatically. A source tarball and .deb package are available on kornelix.com.
     
    Fotoxx Capabilities
      +  Thumbnail browser / navigator with variable size thumbnails and list view.
      +  Camera RAW file conversion 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: brightness,
           color, contrast, tone mapping, trim/crop, resize, rotate, sharpen, de-noise, paint,
           clone, red eyes, add text, warp, HDR, panorama, art effects ...
      +  Select image areas or objects, 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 and view images using any metadata, directory / file names or substrings.
      +  Generate table of image locations and date ranges, click to search and view.
      +  Find images by location by clicking on a map. Add your own maps at any scale.
      +  Create albums (selection of any images). Arrange sequence by drag and drop.
      +  Mashup: arrange images and text in an arbitrary layout (montage).
      +  Slide show: use various animated transitions and slow zoom-in or zoom-out.
      +  Image printing and printer color calibration tool.
      +  Comprehensive user guide and help popups.
      
    Translations
    Translations of the user interface are complete for French, German, Spanish, Catalan, Italian, Portuguese.
    Partial translations are available for Dutch and Russian.
    If you can help with translations, review the topic Translations.
     

    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.
     
    The term directory is used in Fotoxx, and 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 illustration).
     
    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
    Fotoxx needs to know where all your image files are located (directory and file names) and their imbedded metadata (dates, tags (keywords), geotags, captions, comments, ratings). This data is indexed for fast searching. Fotoxx also creates thumbnail image files 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 add roughly 1% to the file space required for your images.
     
    When Fotoxx starts the first time, you are asked to supply two items for the initial file indexing process:

    Top image Directories are those containing your image files, e.g. /home/<user>/Pictures  or similar. Subdirectories underneath your top directories are automatically included, to any depth. Use the [browse] button to locate and add your top image directories, one or more. Other files may be mixed with your image files.

    Thumbnails is the directory where thumbnail files will be placed. These are generally less than 1% as large as typical photo files (10K bytes compared to megabytes). You can use the supplied default or choose another location.

    If you have many thousands of images, the file index function may need significant time. A strong PC (3 GHz, 7200 rpm disk) will process about 1700 images per minute. Some PCs will be much slower. When you add new image files to your collection, the next Fotoxx startup will index only the new images, at the same speed (up to 1700 per minute). If there are no new image files, startup should be fast (about 1 second on a strong PC hosting 10,000 image files). For more details, see Index Image Files.

    User Options
    Here you can make some customizations in the user interface and functionality (link).
     

    Window Views and Menus

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

    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 drop-down list. Hover the mouse over a menu to see popup text with a brief description.
     
    File View (key F)
      +  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 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.
      
    Gallery View (key G)
      +  Use the menus to navigate within the gallery, change the thumbnail size, etc.
      +  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 to go there.
      +  Click a gallery thumbnail: show the clicked image at full window size (File View).
      
    World Map View (key W)
      +  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 collaps the map to fit in the window.
      +  If your image files contain geotags, corresponding red dots will appear on the map
           (geotags come from the camera GPS sensor or can be entered manually).
      +  Click on a red dot to get a gallery view of the corresponding images.
      +  You can choose among other supplied maps, and add your own maps.
     
    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 to move to the previous or next image in the gallery. Other buttons in this view show popup menus which are used to modify the image or perform utility functions. The current image file is a key concept in Fotoxx. This is the file that most of the menu functions will operate on.

     
    Mouse Actions for Image File View
    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 Setings). 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.

    Shortkut Keys for Image File View
    + or =
    zoom-in (amount adjustable in user options)

    unzoom, fit entire image within window
    Z
    toggle: zoom to 1x image size, zoom back to fit window
    ← / → keys
    go to previous / next image in current gallery
      

    Gallery View
    All image files in the current directory are shown as thumbnails. Use this window to scroll around and select image files by clicking thumbnails. The arrow buttons allow scrolling forward or back by rows or pages. Use the zoom buttons to change the thumbnail size and the number of thumbnails fitting in the window. Clicking on a thumbnail will change to the full image view. Pressing the gallery button 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 the image collection.

     
    There are several types of galleries:
      +  Directory: all the image files in a single directory (folder)
      +  Search results: images found by the search function, in various directories
      +  Images in an Album, which may be located in various directories
      +  Recent Files: the most recently viewed or edited images
      +  Newest Files: the images most recently added to 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. Use the navigation buttons (above the thumbnails) to move around within the gallery. Click a thumbnail to switch to image view, where you can edit the image if wanted. Press the gallery button to go back to the gallery view, which is positioned to show the current image in the top row.
     
    The [Sync.G] button (key S) can be used to reset the gallery to the directory of the current image file. For example, if the current gallery is the result of an image search function, it may contain images from multiple directories. If you click on a thumbnail, this becomes the current file which is shown full-size in file view. If you return to gallery view and press [Sync.G], The gallery changes to the directory of the current file.
     
    A gallery thumbnail has a right-click menu with some commonly used functions. These include Popup Image and Popup Image (add). These create a popup window with a larger image that can be rapidly zoomed to any size with the mouse wheel. If Popup Image is selected from another thumbnail, the popup image is replaced. If the (add) variant is chosen, a new popup window is created. Many popup windows can be open at once. This is especially useful for comparing multiple photos of the same subject. If the popup image is zoomed down to a small size, it disappears. Popup Image (add) has a keyboard shortcut: Shift + left mouse click. F11 can be used to expand the popup to full-screen and back. Escape can be used to close the popup.
     
    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.
      

    World Maps 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. Red dots are shown where there are images with a corresponding geotag location (from a camera GPS receiver or entered manually into the image EXIF data). The red dots can be clicked to show a gallery view of the corresponding images.

     
    The Choose Map menu offers a selection of large-scale continental maps, and you can also install other maps (countries, cities, parks, etc.). These all work the same way. Search Range allows you to set a search distance for matching image locations when a red dot is clicked. Images with geotags within the range are selected.

     
    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 Images by Map Location.
     

    General Editing Procedures
    The image in file view mode (the current image) can be modified with the edit functions in the menus Edit, Repair, Color, Bend, 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. The [undo / redo] button can be used to review the before / after results for the current active edit function. Use a left / right mouse click for undo / redo respectively. After an edit function is closed, this button can be used to review all the edits done to the current image. 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 if using a strong PC.
     
    Edit Workflow
    You can minimize the time needed to process many 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.
      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 "overwrite" or "new version" or "new file".
      7.  Press [Next] to start the next file. Press [Next] again to skip if wanted.
      8.  Repeat steps 4-7 for each image. The Trim/Rotate function remains open. 
      
    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, and saving a RAW file as TIFF or PNG is slower than JPEG.
    • Open the RAW file with one of the File > Open RAW menus, or right-click the gallery thumbnail and use one of the popup menus. These are interfaces to specialized RAW editors (currently UFraw and Raw Therapee). Saving the file creates a TIFF-16 file in Fotoxx which may be further processed.
    • Use one of the Tools > Batch RAW menus. 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 or speckles when retouch 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 file size to 10% or less. Note that editing in deep color is more important than having deep color in the final image. You will not be able to see any difference between a 16 bit TIFF / PNG file and a 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.
     
    Mouse Ownership
    Some 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 on any part of the image.
    Dialog 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.
     

    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 some batch functions in the gallery view Tools menu to speed some common tasks.
    You can select any number of image files (link) and apply a batch function to all of them.

    Batch functions can be used for the following tasks:
      +  Rename files (using a base name and sequence number)
      +  Convert file types (e.g. png to jpeg)
      +  Find and upright photos made with the camera turned 90\B0
      +  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
      +  Delete files or move them to Trash
     

    Selecting Image Files from an Image Gallery Window
    This procedure is used for all functions that operate on multiple image files (batch add or delete tags, batch convert images, batch RAW convert, manage albums, 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 at the top or clicking on thumbnails represending 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 file list in the dialog can also be edited directly: you can use cut and paste to get the sequence you wish, but be careful to always cut and paste entire lines (files). The [add_all] button will add all the image files in the current image gallery. After using Search Images to establish a set of images, the gallery window will contain this set. 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.
     
    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 to the clicked image.
     
    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. You can add text and / or icons that link to any menu functions you choose. You can arrange them in a layout window. You can leave this window open and access any function with a single mouse click.
    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.
     
     Image View popup

     View Metadata
    Show short form metadata report
     Edit Metadata
    Edit photo date/time, rating, tags, captions, comments
     Edit Geotags
    Edit photo latitude / longitude, place name, country
     Rename
    Change the file name
     Copy to Location
    Copy the image file to another location (duplicate the file)
     Move to Location
    Move the image file to another location (delete the original)
     Copy to Image Cache
    Add the image file to the file cache for later pasting into an album
     Copy to Clipboard
    Copy the image file to the clipboard (for other apps to paste)
     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 Enhance
    Limited automatic image enhancement
     Retouch Combo
    Adjust brightness, color, contrast, saturation, black point, white balance
     Brightness Dist.
    Adjust the brightness distribution (flatten, broaden, change black and white points)
     Zonal Flatten
    Enhance contrast and brighten shadows, especially image areas with low contrast
     Tone Mapping
    Enhance contrast and apparent brightness range by increasing brightness gradients
     Select Area
    Select an image object or area for separate editing
     Trash
    Move the image file into the wastebasket
     Delete
    Delete the image file
     Gallery View popup

     Popup Image
    Show image in a larger window - resizable, movable, persistent until canceled
     Popup Image (add)
    Same as above, but a new window is opened instead of re-using the previous one
     View Metadata
    Show short form metadata report
     Rename
    Change the file name
     Upright
    Upright the image that is turned 90 degrees
     Copy to Location
    Copy the image file to another location (duplicate the file)
     Move to Location
    Move the image file to another location (delete the original)
     Copy to Image Cache
    Add the image file to the file cache for later pasting into an album
     Copy to Clipboard
    Copy the image file to the clipboard (for other apps to paste)
     Trash
    Move the image file into the wastebasket
     Delete
    Delete the image file
     Popup Image shortcut
    Shift key + left click on a gallery thumbnail creates a popup window containing the image.
    Zoom in/out using the mouse wheel. When zoomed small it disappears.
       
    Keyboard Shortcuts
    Keyboard shortcuts are available for some functions. The notation "Alt+G" means press and hold the Alt key, then press the G key. Many of these can be changed, and new shortcuts can be added (see KB shortcuts).
      
    General

     F / G / W  keys Change view mode: image File, Gallery, World maps
     F1 function key Display user guide for current or prior function
     F11 function key Toggle main window to full-screen (no menu) and back


    Image File View

     left / right arrow keys Previous / next image
     + or =  /  - keys Zoom image bigger / smaller (zoom amount adjustable in User Options)
     Z Toggle: zoom image to 100% / fit image in window
     S
    Sync Gallery to current image file
     M Magnify Image
     P Open Previous File (or toggle between last two files)
     U Undo current edit, or undo one edit step in the current image
     Shift+U Redo current edit, or redo one edit step in the current image
     N Rename Image File
     K View and edit keyboard shortcuts
     Alt+G Grid Lines on / off (toggle)
     T Trim/Rotate Image
     V Voodoo Enhance
     R Retouch Combo
     Escape key Exit a dialog, exit Slide Show mode
     Space Bar Pause / resume a running slide show


    Gallery View

     Home / End keys move to first / last page of image gallery
     Page Up / Down keys move to previous / next page of image gallery
     up / down arrow keys move up / down by one row of image gallery
     left / right arrow keys move to previous / next page of image gallery
     + or =  /  -  keys bigger / smaller thumbnail size
     Escape key exit a dialog
      
    Mouse Functions
     left click Zoom-in: magnify image, center at click position
     right click Zoom-out: restore image to window size. If no zoom, popup menu with common functions.
     mouse wheel Zoom in and out depending on wheel direction
     left drag on image Pan / scroll zoomed image, same direction or magnified opposite direction (like scroll bars)
     right drag on gallery Scroll down or up by dragging to the top or bottom edge, faster as mouse approaches the edge.
     mouse + Ctrl key
    Mouse acts on main window instead of a mouse-using dialog like Select Area.
      
    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 by the user to organize the images is required. Search methods include directory and file names (or partial names), image dates, image ratings, tags (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 image management applications. Options for how to organize a large image collection can be found in the topic Organizing Images.
     
     
     
    Menu Functions
      


    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.
     
     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. The configuration file can be edited directly - at your own risk. 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 most image edit functions can be found here:
      /usr/share/fotoxx/icons/edit-funcs
     
     
     


    File Menu
     

    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, the final result is the file saved last.
     
     
    Sync Gallery
    Initialize the current gallery from the directory of the current image file (link).
      

    Recently Seen Images
    The 100 most recently seen image files (viewed or edited) are shown in a gallery, from which you can select a file to open.
     

    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
    This function starts a standard file open dialog, allowing you to select an 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. Drag and drop can also be used to open a file: drag an image file from Nautilus (or other source) to the Fotoxx window, and Fotoxx will open the file. If text is dragged from gedit (or other program with drag sourcing), Fotoxx will assume the text is a filespec and try to open it. Thus you can make a list of filespecs in a text file and use this list with Fotoxx. Effectively, you can use Nautilus or text files to navigate images as an alternative to the Fotoxx navigation system.
     
    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).
     

    Open Previous File (Key P)
    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.
     

    Open RAW File (ufraw)
    Open RAW File (rawtherapee)

    Select and open a RAW camera file using either Ufraw or RawTherapee.
    You can make adjustments to the RAW file such as color temperature, white balance, brightness, color, noise, etc. from the program GUI. When the file is saved, a TIFF file is created with 16-bit color depth. 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 Options.

    Raw Therapee does not allow the location and type of the output file to be determined from a calling program, therefore you must save the output file where Fotoxx can find it - in the same directory as the RAW file. The file type must be TIFF (.tif) with a color depth of 16 bits.
     

    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.
      

    Rename Image File  (key N)
    This function can 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.
      

    Trash Image File
    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 (some distros are like this), you are given the option to delete the image file.
     

    Delete Image File
    The current image file is deleted permanently. There is no recovery possible.
     

    Print Image File

    The print menu brings up a standard Page Setup dialog where you can select a printer, a paper size (letter, A4, etc.), and orientation (portrait or landscape). Select your printer (do not use a default setting). The paper size shown will be the one last set in the Printer Properties dialog (a separate admin function in the System menu). If the paper size is wrong, fix it using the Printer Properties dialog. Changing the paper in the Page Setup dialog may be ignored or lead to a "paper mismatch" failure. 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 in centimeters) 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 qality inputs, and a preview of the printed layout which can be accepted or rejected.

    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


    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 Options).
     
    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 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 Bend 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 Options).
     
    File sizes for a 10-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
    70 MB 35 MB 23 MB 17 MB 8 MB 3 MB 2 MB 1 MB
     
    The default JPEG quality is used unless you change the value in the save-as dialog. The default value is a User Options option, and is initially set at 90. You will not be able to see a difference between a file saved with quality=90 and one saved 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
    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.
     
     
     


    Metadata Menu
     
    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)
    View Metadata (long)



              Short Report














    Long Report

    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.

    Fotoxx uses the following EXIF / IPTC data items:
     
    Key Name Fotoxx Usage
    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
    Caption-Abstract Edit Metadata function
    Geotags Edit Geotags function, 3 search image functions
    Image History
    History of Fotoxx edits applied to the image
    any key Edit Any Metadata, Delete 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 switched ON, captions and comments are also displayed during a Slide Show.
     

    Tags Overview
    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, holidays, 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, given 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 search for a tag that does not exist in your images.
     

    Edit Metadata

    The menu Metadata > Edit Metadata is used to edit the most frequently used metadata: image date and time, rating, caption, comments, 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 file versioning for metadata changes unless you do this yourself by saving a new version and then modifying the metadata.

    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, press Enter 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 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 an optional Stars rating for the image. The dialog remains open if you navigate to a new image, and the data is updated from that image. The [Apply] button writes the data to the image file and to the metadata index file used for searching images.
     
    Manage Tags
    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]. The category can be left blank and 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.
     

    Edit Any Metadata

    This is a dialog for editing any EXIF or IPTC metadata. Enter the desired key name and press [Fetch]. Change the returned data, if any, and press [Save]. The metadata is updated. 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
    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.
      

    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). After selecting files, specify tags to add to the images by clicking tags in the Defined Tags list. If you need to create new tags, use the [manage tags] button as described above. When done specifying image files and tags, press [proceed] to add the tags to the image files.

    Removing tags works much the same way: select the image files and the tags to remove. You can add and remove at the same time if the images to process are the same ones. 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.
     
    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.


    Batch Rename Tags
    With this function, you can rename any number of tags and apply these changes to your entire image collecton. Prepare a text file with a list of tag names and their replacements. Each line has a pair of names separated with a comma (old tag name, new tag name). When the function is started, you will be asked to open this file. The list of tag names is output to a popup scrolling window, followed by a list of images that have any of the tag names to be revised. Inspect this list carefully. If OK, press [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 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 dialog (above right) and you can proceed or cancel at this time. The [Short List] button presents a list of commonly used key names. 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.
     

    Geotags Overview
    Modern cameras can record the location of each photo, using an internal GPS receiver. Latitude, longitude, city and country are recorded in the EXIF metadata of the image JPEG or RAW file. The Fotoxx Edit Geotags function (immediately below) allows location data to be entered or revised for any image. The Fotoxx Search Images function can find images by location, including an optional range: e.g. find all photos taken within 30 km of London. Locations may also be specified by clicking on a map. There are two functions that can find all images from a specified location or region: Images by Geotag: find all images for a country, a country and city, or a country, city and date range. Images by Map Location: click on a map to show all images within a range of the clicked location.
     

    Edit Geotags

    The dialog displays the location data for the current image, if any. The live display is updated when a new image is opened. For an image with missing or incorrect location data, enter a city name and use the [Find] button to either complete the data in the dialog, or get a list of matching cities to choose from (e.g. London, United Kingdom and London, Canada). 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. If the data is revised in the dialog (spelling of city / country, or revision of latitude / longitude), this will take precedence for future city searches. Use the [Prev] button to fill the dialog data with the last location used. If the [Find] button does not find a city (it is not present in any other image), you can use the [Web] button to find the city and 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. These names are not standardized and there are many duplicates, 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 using the [Find] button. If the [Web] button fails, you can manually find the city in the internet and enter the location data into the Edit Geotags dialog. Pressing [apply] will add the data to the current image and make the location available for future use.

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

    Blank the latitude and longitude if you want to save only the city and / or country name in the image file. If the latititude / longitude data is changed from the values returned by [Find], these values are saved and will be used from now on for new entries for this city. Previous images with the same city are not revised. You can use Images by Geotag (see below) to find all images for a city, and Batch Add Geotags (see below) to change the latitude / longitude for all of them.

    Note: If the location name is not a city (e.g. Yellowstone Park), simply use this name for the City input.

    Note: 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 returns periods.

    Note: If a city is saved without latitude / longitude, there will be no "red dot" to mark the city on a map, and finding photos for this city by clicking on a map will not work (see below, Images by Map Location). The other method to find photos by city (Images by Geotag, see below) will still work.

    The [Map] button changes to world maps view mode. Zoom-in to the area you need and click on a map position. The closest known city (within the specified range) will be selected. Use [Apply] to save the location into the image file.

    For a series of photos made in the same location, you can quickly add location data. After getting the location data for the first image, use the [Next] button to open the next image, then the Edit Geotags [Prev] button to fetch the previous location data, then the [Apply] button to save into the current image file. If there are many photos, you can use Batch Add Geotags (see below) to quickly update all of them.
     
    Summary:
      +  Open an image file and select the Edit Geotags menu.
      +  Enter (or change) a city name (possibly abbreviated) in the dialog.
      +  Use [find] to find the city 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 city.
      +  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.
      +  Open the next image (use the [prev/next] button if appropriate).
      +  Use the [prev] and [apply] buttons to add the same location data again, if wanted.
     

    Batch Add 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 Geotags. Press [proceed] to start the update process. Use this function also to correct city / country spellings or latitude / longitude data, or to fix inconsistencies.
     

    Images by Geotag (report)

    This is a fast way to find all photos made in a given country or city and country. In the dialog, select the desired level of grouping: by country, by country and city, by country and city and date. In the last case, you can select a date range for grouping of images having nearby dates. A number N will group images 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 cities found in the image geotags, and the dates of photos taken in those cities. The count of photos taken is also shown. In the above example, 9 photos are from Grossglockner starting Oct. 19, 2005. Click on a line in the window to get a thumbnail gallery of those images, and from there you can click on any image to view or edit.
      

    Search Images (report)
    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.

    An example search: find all images dated 2006 or later, rated with 4 stars or more, having tag "buildings" and geotag "Dresden". The output report for this search is shown here:



     
    Search Images Dialogs

    The main dialog is on the left. The Geotags dialog appears if the [geotags] button is used to select location data, and the Metadata dialog appears if the [other] button is used to select metadata that is not available in the main dialog.

    In the main dialog, select which images to search, either all (the entire image database) or current, meaning the images in the current gallery list (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, 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 metadata selection dialog.

    Enter your search criteria. Select desired tags, dates, star ratings, text (comments, captions) and file or directory names. Available tags are shown in Defined Tags and can be chosen with point and click. Use the radio buttons all or any to indicate if all or any of the given values must be present for an image to be selected. 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).

    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. The format is yyyymmdd. 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 yyyymmddhhmm. 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 also 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. Name matching is not sensitive to case, and substrings will also match.
     
    Image comments and captions may also 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.

    The radio buttons all and any apply to tags, text, and file names. You can select images having ALL the entered strings, or ANY of the entered strings. Example: if the search file field contains [egypt cairo] and any is selected, then image files with either of these names within the directory or file name would be selected.

    Geotags Dialog
    The [geotags] dialog selects images by location. Enter a city name and press [find] (as described in Edit Geotags) to get country and latitude / longitude added automatically. Enter a kilometer range to determine the max. distance of images from this location to be included. If latitude / longitude and kilometer range are present in the dialog, these are used to locate images by latitude / longitude. If you want to search only by city and / or country name, erase latitude / longitude and leave the city and / or country. Use the [map] button to change to map view. Click on the selected map to set a latitude / longitude to search. You may enter "null" for city, country, or latitude / longitude to find images lacking these geotags.

    Search Metadata Dialog
    You may use this dialog to search for 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" (my Panasonic) then only the images taken with this camera will be reported. You can also enter multiple match values for one key, separated by blanks.

    Performance
    If no extra metadata to report is entered, then thousands of images per second are searched and reported (assuming a strong PC). If extra metadata is entered, the search performance slows to something like 90 images per second. This is because the image files are being read to extract the metadata not included in the metadata index. For good performance, always use dates, file names, etc. to maximize the use of the metadata index and minimize the number of image files that must be read.

    There is a separate topic on image organization options.
      
      
     


    Area Menu
     

    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, opened and pasted into other images, and edited there.
     

    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 Drawing 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 and visibility, regardless of the brightness or color of the background image.

    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 object 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 color.
     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 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 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.

    Follow Edge
    High-contrast pixels (likely object 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.

    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 more / fewer pixels.

    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.

    The firewall checkbox can be used to stop the search wherever already-selected pixels are met. Use this if the border of an area has already been defined and you want to stay within this border.

    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
    When you are finished selecting the enclosed spaces, use the [Finish] button to complete the process. A popup dialog will ask you to click the mouse inside each enclosed space in sequence. This action launches a search for all pixels within the enclosed space, and these are mapped and saved. The enclosed space is temporarily colored so you can see exactly what part of the image is being selected. The dialog will show the status of the search, "success" or "outline has a gap". If there is a gap in the outline, an attempt is made to show where the gap is: you will see a line coming out of the colored space to meet the edge of an imaginary rectangle enclosing the space. You may be able to follow this line back to the gap, but if the area is convoluted finding the gap can be difficult. Each use of [finish] will produce a different picture that may lead you to the gap. Carefully inspect the outline of the area, close the hole, and use [Finish] again. An area is not effective for edits until it is successfully finished. 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 clicked.

    If you select any of the four extend to corner options, the area outline is automatically extended to the corner from drawn lines that meet the image edges. This avoids the work of drawing the area outline along the image edges to the corner. Example: to select the sky, draw the boundary line between sky and ground, meeting the left and right edges of the image. Then select the upper left and right corners in the Finish dialog. The entire sky area will be selected, without having to outline the rest of the sky.

    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.
     

    Area Show / Hide
    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
    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
    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
    Permanently discard the current area. This is also available as a button in the Select Area dialog.
     

    Area Copy and Paste
    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 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
    A selected area can be saved to an image file using the menu Select > 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 Select > 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.
     
     
     


    Edit Menu
     

    Trim (Crop) and Rotate Image (key T)

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

    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 corner of the rectangle to move that corner. The dialog box shows the current width / height ratio of the selection rectangle. If the box lock ratio is checked, then moving one corner of the rectangle will also move the opposite corner to keep the same ratio. 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 corner of the selection rectangle in 1-pixel steps. The last 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 black margins left over from composite and warp functions. These functions leave black margins where images did not overlay or were bent away from the edge. [Auto] tries to find a maximum rectangle that does not overlap any of the black 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 tilted 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 auto-trim option automatically cuts off the edges (making them perfectly horizontal and vertical) when an image is rotated a small amount to make it level.

    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

    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). This function is available in the right-click popup menus for the main image window and gallery thumbnails.
      

    Voodoo1 (key V)
    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.
    Voodoo2
    This is an alternative automatic enhancement, using the zonal flatten method described below.
      

    Retouch Combo (key R)
    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
    x-axis is initial brightness, y-axis is revised brightness
     
    This example shows a reduction of brightness for
    darker image areas, and an increase for brighter areas.
     
     




    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 for white balance
    enables the mouse to click on a black point or gray / white point
    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). If you click on a fairly bright gray or white spot on the image, this will be used as a white balance set point, and the image 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. If you click on a very dark spot on the image, this will be used as a black set point, and the image RGB colors will be shifted to make this spot black. This is one way to reduce fogginess in a photo, or make the background sky look black instead of gray in an astronomy photo. The All curve base node will be shifted to the right to reflect the new black point.

    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.
      

    Adjust Brightness Distribution

    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.
     

    Zonal Flatten

    Zonal Flatten enhances visible detail in areas having poor contrast. It is similar to Flatten, described above, but is more effective for an image already having a wide overall brightness distribution. The revised brightness for a pixel is based on the brightness distribution for nearby zones. A larger zones value 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 to correct this, use Select Area and Denoise to fix the noisy areas. Alternatively, use Select Area beforehand to select sky (or other areas to omit) and then invert the selection prior to using Zonal 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 if the pixel were really a member of the nearby distributions. 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.
      

    Tone Mapping
    Tone mapping 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. Tone Mapping 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. Tone mapping 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. Tone mapping 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 vegitation.
      

    Resize Image (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.
     

    Flip Image
    Choose either horizontal or vertical flip from the dialog. The image is reversed (mirrored) vertically or horizontally. Repeating the flip restores the original image. Doing both a horizontal and vertical flip is the same as a 180 degree rotation.
     

    Add Text to Image

    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]. Case and embedded blanks do not matter, so 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

    This function writew 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. 
     

    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 leave the controls in a neutral position, (2) start the Paint Edits dialog, (3) drag the mouse over the desired areas and watch the effect, (4) adjust the edit function controls, (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, Zonal Flatten, Tone Mapping, Sharpen, Denoise, Adjust HSL, Color Depth. Others may be added in the future.
      

    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 for 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 tone mapping primarily to dark pixels: Start Tone Mapping, 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 tone mapping 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

    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
      

    Sharpen Image
    This function has three methods to sharpen a blurry image.

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

    Gradient
    : steepens brightness transition areas directly, somewhat like tone-mapping.

    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 details can be lost.

    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
    This function can be used to blur or un-sharpen 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 limit the blur to a face or part of a face.
     

    Denoise Image - reduce noise

    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 image 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. Move it back to the left to process increasingly darker areas only.

    The [measure] button starts a dialog to measure the actual noise level. Click on the image to show the RGB noise levels in the area of the mouse. This must be a featureless area so that noise is the only variation present. A dark gray sky is a good source, or a badly out-of-focus area. To measure camera sensor noise, use a RAW image. A JPEG image has already been processed inside the camera to reduce noise. More information can be found in the technical notes.

    Here is a short technical description of each method:
     
     Flatten 1 The highest and lowest pixel values within a radius are moderated slightly.
     Flatten 2 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 a band of pixels at a distance.
    The distance is increased in steps from 1 pixel to the radius limit.
     Wavelets Image brightness (with noise) over distance is converted into a series of wave functions that nearly sum to brightness and represent an approximation with less noise.
     Threshold 
    Uses the threshold value to distinguish pixel noise from image features, and an algorithm to adjust initial pixel classifications based on neighboring pixel classifications.

    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
      

    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.
     

    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.
     

    Paint / Clone

    This function changes individual pixels by painting them with the mouse.
    If a select area is enabled, the painting is confined within the area.

    Paint Color
    This button allows you to pick a color, and shows the current color. You can also shift + left-click on the image to choose a color from the image. The paintbrush radius 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 (gradually undo the painting). The transparency controls determine how intensely the color is applied (or erased) at the center and edges of the circle. Zero transparency applies the full color immediately whereas a high transparency (90+) allows you to gradually change the color using multiple drags (analogous to spray painting from a distance). Erase also works this way: use zero transparency to immediately erase, and high transparency 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.

    Copy From Image (clone)
    Instead of one fixed color for all pixels within the paintbrush radius, pixels are taken from somewhere else in the image. Shift + left click on the image to select the source area, then drag on the area to paint. The source area is painted over the dragged area. The transparency controls work as described above. This method can be used to erase an unwanted object, replacing it with background taken from elsewhere.

    The [undo-last] button removes the last paint or erase operation, and this can be repeated to remove many recent edits. Each new drag operation is a unit of work that can be separately erased. The memory for undo operations is limited to one gigabyte, which can be reached if you make many edits using a large brush (every change to every pixel is saved). It is useful to save the image after each satisfactory change to free this memory.

    The gradual paint option functions as already described. If you de-select this option, painting and erasing will be sudden instead of gradual. If you are painting within a select area with edge blending, the painted changes will be zero at the edges and 100% for pixels beyond the blend width from the edges. This works better if gradual paint is not selected.


    Paint Transparency
     
    This function can be used to 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.

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

    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 (a modified black / white conversion for an aged photo effect).

    Select one of the four buttons:
        black / white positive - convert a color image to black and white
        black / white negative - convert to black and white and reverse brightness
        color positive - do nothing at all, or undo one of the others
        color negative - replace each RGB color with its compliment
        sepia - convert to a modified black and white for an aged photo effect

    Color negative: Each RGB color is replaced with the maximum value - the 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. This produces complimentary colors as follows: red becomes cyan, green becomes magenta, and blue becomes yellow.

    Sepia: This is like a conversion to black and white, but a shade of brown called sepia is added to the shades of gray. This makes a photo look like a very old photo from the early days of photography. Many cameras and photo edit programs have this feature.
       

    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.
      

    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) 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

    Change the color of an image or selected areas using an HSL color chooser. You can choose a color hue and adjust saturation and lightness independently. This color is blended with chosen image colors using the adjustment slider. 0% means no change and 100% means complete replacement. Select a color to change by pressing the shift key and clickiing on the image. Color changes will center on this color. All image colors are compared to the selected color and changed according to the degree of match. Color match defines how close the colors must match: 0% means no match (all colors are changed), 100% means only colors very close to the target color are changed. You can move any slider and watch the changes in the live image. This function is useful for replacing selected colors, e.g. an overexposed sky (almost white) can be made to look realistically blue. This function can also be used with Select Area to change only selected image areas.
     

    Brightness Ramp


    This example shows brightening of the
    upper right image quadrant.



    This function varies the brightness across an image, with the direction and magnitude of the brightness slope determined by editable curves. You can use this to compensate for uneven lighting or vignetting (darker image corners). The function dialog displays two editable curves, horizontal and vertical. The horizontal curve adjusts brightness horizontally, and the vertical curve adjusts brightness vertically. Move the curves in the directions labeled "+" and "‒" to increase or decrease the image brightness in the corresponding image area. To remove vignetting in the image corners, move both ends of both curves in the "+" direction while fixing the middle areas or even moving them in the "‒" direction. To brighten the upper right corner, move the right end of the horizontal curve and the upper end of the vertical curve in the "+" direction, as in the example above.

    If used with select area, the scales refer to the enclosing rectangle of the area instead of the whole image. Thus you can select an area of an image and apply a brightness ramp across the area. If the button All is selected (default) then all colors are adjusted equally (i.e. brightness is adjusted). If one of the colors is selected, the image is adjusted for that color only, and the All curve is ignored. Any or all three RGB colors may be adjusted in this manner. You can use this to remove a color-caste that varies across an image or image area.
     

    Color Ramp

    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, with the most recent point at the top. The points are labeled A-I in the list and on the image window. 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

    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 Profile
    Use this function to change from the normal sRGB color profile to some other RGB color profile. If you have images with Adobe RGB color, you can change them to sRGB for display on a monitor using sRGB (normally the case). You may need to install ICC color profiles. In Ubuntu, the package names are icc-profiles and icc-profiles-free.
      

    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

    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. The resulting image is 2x the size of the original. 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. Pixelation in photos normally occurs only when a photo has been reduced and then enlarged. Pixelation is not a problem with normal photography - lens blur is generally larger than the camera sensor pixel size.
     

    Color Fringes (chromatic abberation)

    This function is used to reduce chromatic abberation. 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.
     

    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. 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 trimmed, or if exactly the same trim was applied to both images. 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 paper sheet or blank wall that is underexposed to come out gray. This image can be used to find both bright and dark stuck pixels.
     
     
     
     

    Bend Menu
     

    Unbend Image

    Panoramas of nearby subjects (typically buildings or interior rooms) may show straight lines that are curved, or buildings that are slanted. Bending of the images is necessary in the panorama process in order for the images to fit together. For landscapes this may not be noticeable. The unbend function can be used to straighten the image. 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. Input values for horizontal and vertical unbend and watch the effect on the image. Increase or decrease the values and repeat until satisfied. Move the axes to change the centers of unbending. The linear values will slant the image left / right or top / bottom edges to remove slant. The curved values will straighten the image curving that comes from making a panorama. See also Warp Image for another method of correcting image curving and perspective.
     

    Fix Perspective

    This function can be used to straighten a photo made from an offset angle. The painting on the left is the original photo, taken from below and left of center, to reduce reflections. The painting on the right is the straightened version. This function can also be used to straighten a building photographed from below or from the side.

    Click on the four corners of the tetragon shape 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 labeled with small letters A, B, C, D. The upper left corner of the square enclosing the letter precisely marks the corner position. Clicking near a corner will move it to the new position. After the 4th corner is defined, a new click replaces the closest corner.

    You can use the keyboard arrow keys to move the corner markers in 1-pixel steps. The arrow keys work on the last corner selected or moved with the mouse.
     

    Warp Image (distort)

     

    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. The method used limits loss of resolution from repeated warps: for each warp step, the total movement of each pixel is accumulated and the original image is warped to the latest pixel positions. The pixels are interpolated to reduce jaggies and improve sharpness.
     

    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
    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, pull only on the image corners.
     

    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

    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 unsqueezes 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 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 edited dots, which can now be adjusted.
     
     
     
     

    Effects Menu
     

    Color Depth
    This function changes the normal 16 bits per RGB color (red, green, blue) 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.
     

    Pencil Sketch

    This function transforms a photo into something like a pencil 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.
     

    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

    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


    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 sparkel in the eyes. Taken to extremes, it produces a "cartoon" effect, especially when used in combination with other retouch and effects functions.
     

    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
    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

    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
    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
    This function is 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.
     

    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

    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.

    Width and Height are initially set to the size of the pattern file. You can adjust these smaller, which will cause less of the pattern to be used for duplication. 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. Use Width and Height to revise which part of the pattern image is used. 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 Tone Mapping to add a 3-D effect to the pattern.

    Useful pattern files in LibreOffice: /usr/lib/libreoffice/share/gallery/www-back
     

    Mosaic
    Create a mosaic image using tiles made from all your images.


    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 10K images / min. on a strong PC). If you change the tile dimensions, press [Tiles] again to regenerate the tiles. This may work much faster since the images have been cached in memory by the OS. The generated tiles are saved to a file and loaded again the next time the Mosaic function is used. Regenerate the tiles only when new images have been added to your collection or if you change the tile size.

    After the tiles are created, press [Image] to convert the current image into a mosaic using these tiles. This takes only a few seconds. You can process additional images without regenerating the tiles. 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. Use the mouse wheel (or CTRL+left click) to 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

    Apply a custom convolution kernel to an image (a small matrix useful for blurring, sharpening, embossing, edge-detection, and more). The underlying technology is explained in Wikipedia. Input a kernel size, a divisor, and fill-in the table values. The values can be saved into a file and retrieved later by using the [Load] and [Save] buttons. [Reset] restores the original status. [Apply] applies the kernel to the image, and can be repeated for multiple applications.
     

    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 seleced area.

     

    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.
     

    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).
     
     
     


    Combine Menu 
     

    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, 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 quasi-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. You will likely need practice to become effective at working the curves. A faster and easier alternative may work as well: after the images are combined, ignore the curves and exit from HDR. Use various edit functions to refine the image: Brightness Distribution, Retouch Combo, and Tone Mapping. Select Area can be used to enclose any area in the image which needs more brightness, color, or local contrast, so you can apply different methods and parameters to different areas.
     

    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. 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
    Combine (overlay) multiple photos of the same subject taken at different times. Remove tourists and cars that come and go between shots by painting them away with the mouse.

    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 at least one photo with each part of the subject not obscured by the moving objects.
    Processing the photos: In Fotoxx, choose the Stack / Paint menu function and select up to 9 images. 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. 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 in the image, choose an input image that is free from the moving objects. The radius of the paintbrush can set larger or smaller, so you can paint large areas quickly and control fine detail when needed.
     

    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. 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. A few alternative tools 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
    This function stitches 2-4 images together to make a wide image or panorama. The images must overlap by 15% or more, so that the program can find where they coincide and join them together. Start by selecting 2-4 image files. The images are initially joined and shown with a small transparent overlap. A pre-align dialog 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 or downloaded 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 button is normally unchecked. Its purpose is described below.

    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 match is found. This may need a minute or more for a weak PC working with large images. 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.

    When done, you can use unbend, warp, trim/rotate and other functions for final adjustments.
     
    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 position 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 only a small error.
     
    Mouse Warp
    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.
     
    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 in the overlap areas, 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 in the overlap areas. Sky can be easy to fix by selecting the false areas and copying sky from elsewhere in the image (see Paint / Clone). 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
    The Panorama Tools utilities (via the Hugin package) have been integrated into Fotoxx under the menu PT Panorama. The user interface is very simple: specify 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 name>-PT.tif. This is an 8-bit TIF file and is very large. You can save the file as JPEG and delete the original TIF 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.
     

    Mashup
    Arrange multiple images and text in a layout (photo montage). 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 reposition 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. bend or warp. These will be made transparent. The Margins controls can be used to increase edge margins. 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 reposition 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 reposition 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 HOME, then select .fotoxx/saved_areas. Open any of the .tiff 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 to add 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 algnment 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.
      
      
     

    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 edit functions were done 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)
    This allows you to review "before" and "after" images in sequence for each edit done to the image.
      + middle mouse click pops-up a list of all edit steps - select any step to go back to.
      + use the escape key to abandon the list without making any selection
     
     
     
     

    Tools Menu
     

    Index Image Files

    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. A strong PC can process about 1700 image files per minute, but some PCs will be much slower ("strong PC" means a 3 GHz multi-core CPU and a 7200 rpm disk). If there are no new image files, indexing should complete quickly (my strong PC, with 9000+ image files, needs <1 second).

    Image files modified or moved within Fotoxx are taken care of automatically. The Index function is only needed when new image files are created from outside Fotoxx (e.g. a new batch of photos is added into the directories used by Fotoxx), or if files are moved or renamed from outside Fotoxx.

    Index Image Files starts automatically at Fotoxx startup. It can also be started manually from the Tools menu. Unless there are hundreds of new images to process, this will be done in a few seconds.

    Enter your top image directories (e.g. /home/<user>/Pictures). Enter the directory paths directly in the window, or 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 hundreds of thousands of 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.

    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 Photoshop or other apps), these will also be searchable. After the images have been indexed, searching them by any of the aforementioned criteria is almost instantaneous. Any other items in the image metadata can also be searched, but at a slower speed. See Search Images for details.
     

    User Options
    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 window content when Fotoxx is started.
    Recent Files: the most recently viewed or edited image files (gallery display).
    Newest Files: image files most recently added to the Fotoxx database (gallery).
    Previous Gallery: the directory of the last image viewed (gallery).
    Previous Image: show the last image viewed.
    Blank Window: start with no current image and the top image directory in the gallery.
    Directory Gallery: the given image directory.
    Image File: the given image file.
     Browse
    Opens a dialog to browse for the starting directory or image file (last 2 options above).
     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).
     Image Pan/Scroll
     (zoomed image)
    Drag: image moves with the dragged mouse.
    Scroll: image moves against the dragged mouse (like invisible scroll bars).
    Magnified: movement is magnified: multiple drags for large movement are not needed.
     Zooms for 2x
    Choose 1, 2, or 3 zooms for each 2x increase in image size.
     JPEG quality The default quality value when saving an image as a jpeg file type.
     Thumbnail size
    The pixel size for gallery thumbnail width/height: 128 | 256 | 512 (default 256). 512 is a bit slower but better for a large monitor or one with a high DPI resolution, or when viewing large galleries. If this is changed, you need to delete your thumbnail files so that the file index process will rebuild them with the new size (next Fotoxx startup).
     Curve edit 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. Flexibility for extreme curve bending will be less.
     hidden directories 
    Show hidden directories in gallery view. If not checked, these are not displayed.
     prev/next version
    Show only the last versions of image files when using the prev/next button or KB keys.
     RAW command The command used to convert camera RAW files to tiff-16. This can be changed if you need.
    See the man page for dcraw for more details.
     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.
      

    Keyboard Shortcuts (key K)

    This function is used to view or change custom keyboard shortcuts. The currently assigned shortcuts are shown in the window. 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 drop-down list. To remove a shortcut, select it and press [Delete]. If you press [cancel] or [x] all changes will be discarded. The [Reserved] button shows a list of reserved shortcuts that cannot be used here.
     

    Show Brightness Distribution
    This function opens a small window that shows a brightness distribution graph of the current image in the main window, 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 [Red] [Green] [Blue] and [White] to select the colors to show. White means all colors added together.
      

    Grid Lines (key Alt+G)

    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 - Change Color of Foreground Lines
    Some functions draw lines over the image (Trim/Rotate, Area Outlines, 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 conviently switch among the available colors (black, white, red, green).
     

    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 nan -7 -6 -5 -4 -3 -2 -1 0 1
     

    Magnify Image  (key M)

    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. Use the M-key shortcut to start or end the Magnify function.
      

    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

    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

    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 upper and lower bands at 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. Do not use.
      

    Change Language
    This function allows you to change the GUI to one of the available languages. If your language is not available or has missing translations, consider making a translation. See menu Help > Translations.
      

    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".


    Printer Color Calibration
    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. The print will have some false colors caused by the printer. This print 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 (4913) can be used to calculate all in-between colors.
      Each image 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) and vertical paper orientation.
    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 <filename>.png. Use a name indicating the printer settings and type of paper.
    4. Process the edited chart file to create a color map file <filename>.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 to use.
    Once you have made the color map file, you can print any image using step 5 only.
    There is also a File 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.
    • Edit the scanned chart image file. Be sure "TOP" is at the top. Trim off the margins surrounding the color tiles. Use the fat green 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 58 equal columns and 85 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.
    • Scanners may skew the scanned image into a non-rectangular form. If this happens, you will notice it when trying to trim off the margins: the fat green 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.
    • If you notice any black or white spots in the scanned chart image (from dust), fix them with Paint/Clone.
    • 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 more 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 paper back to them (along with some money), and receive an ICC profile, a file which you can install. Perform a web search for "icc profile service".
     

    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 here
        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
     
    Quick Guide
    This is a 1-page introductory document with Fotoxx essentials.

    User Guide  (key F1)
    The user guide (this document) is displayed (created using the WYSIWYG HTML editor Kompozer).

    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.

    Edit Functions Summary
    A one-page "quick reference" summary of the image edit functions is displayed.

    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.

    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.
     
     
      


    Sync Gallery 
    Replace the current gallery (recent files, search results ...) with the directory of the current image file: the image shown in the File View window. Click on any thumbnail in the gallery to set the current image from that thumbnail, then select the Sync Gallery menu (button) to set the directory and generate a new gallery from that directory.
      
      
     
     

    Albums 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 image album is simply a list of the 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 unique image file, so any other way to view this image will show the same image.
     
    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.
      
    Manage Albums Dialog
    New: Start a new album or replace an existing one. The dialog shown immediately below is started.
    Choose: Choose an album to view or edit. The gallery window will show its current images. Use the thumbnail popup menu (below) to add or remove images.
    Images: 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. You can also use Search Images to create a gallery for selecting images, or choose an album gallery. Selected images are added to the image cache. The cached images can now be added to an 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

     
    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.
    Copy to Clipboard
    Copy the image to the clipboard (for other apps to paste).
    Paste Image Cache Here (keep) 
    Insert all cached images at the clicked position. Click roughly between two thumbnails where the images will be inserted.
    Paste Image Cache Here (clear) 
    Same as above, but the cache is cleared.
    Paste Current Image File Here
    Inserts the current image file at the clicked position.
    Remove from Album
    Remove the clicked image from the album
    Popup Image
    Pop-up a large resizeable window for the image. Replace previous popup window.
    Popup Image (add)
    Same as above, but keep previous popup window and create a new one.
    View Metadata
    Metadata short report for clicked image file.
      
    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.

    Summary
    • Make a new album: Use the [New] button and provide a name.
    • Add images to an existing album: Use the [Choose] button and select an album. Use the [Images] button to select images from any gallery. After closing this dialog, the gallery reverts to the chosen album. Right-click an album thumbnail and select one of the Paste menus to insert the selected images. You can also choose another album and insert the images if you did not clear the cache.
    • Remove images from an album: Right-click each thumbnail, then select the Remove menu.
    • Move images within an album: Use the Cut or Copy menu and the Paste menu to move multiple images. To move one image at a time within a single album, you can use mouse drag and drop.
      

    Slide Show
    With this function you can show a pre-selected sequence of images in full-screen mode.
    There are three dialogs used to define and customize 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, press [Select] and choose an album from the list provided. 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.

    The spacebar can be used to pause and resume between slides. The B-key can be used to blank the screen and pause the show. Press again to restore the current image, or press the spacebar to resume with the next image. You can use the left and right arrow keys to go back and forth within the sequence of images. You can interrupt the slide show by pressing the ESC key for a gallery view. Click on a thumbnail image, and this will be the next image to show. This allows you to skip around more easily than stopping and starting the show each time. The M-key can be used (whenever the slide show is paused) to start the magnify function to view selected parts of the image at higher size.

    Dialog Controls:
    Seconds: The standard time each image is shown.
    This can be modified per image with the Images Preferences dialog.
    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.

    Press [transitions] to start a dialog 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 PCs (some may be too slow on slow PCs, but this cannot be helped). The preference parameters specify a relative preference which will influence how frequently the transition type is used when random sequence is selected. The random method avoids using a transition that was used shortly before, and will only work if 5 or more transitions are selected.

    Press [image files] to start a dialog for image preferences. These are optional. An image is selected for customizing by clicking its thumbnail (press the [gallery] button or G-key to show thumbnails). 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, before 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. Zoom location determines the center of the zoom, expressed as a percent of the image width and height (50/50 is the center of the image). These are set by mouse-clicking 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:
      +  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 (if the zoom size is > 1)
      +  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
     

    Set Desktop Wallpaper
    The desktop wallpaper image is set from the current Fotoxx image file. No inputs are required. Note: this function works only for the Gnome window manager (including Ubuntu). Other window managers will not work.
     

    Cycle Desktop Wallpaper
    The desktop wallpaper image is changed at regular intervals, using a Fotoxx album as the list of images to show. Fotoxx runs in the background without a window or GUI interface. It has no effect on normal interactive use of Fotoxx.

    How to cycle the desktop wallpaper:
    • Create an alternative name for the fotoxx executable (e.g. fotoxx-wallpaper):
         $ sudo link /usr/bin/fotoxx /usr/bin/fotoxx-wallpaper   (needs to be done only once)
    • Start  fotoxx-wallpaper, passing the album name and the time interval in seconds:
         $ nohup fotoxx-wallpaper -cycledesktop albumName seconds >/dev/null &
    • To stop the background process:  $ pkill fotoxx-wallpaper
    Seconds must be at least 10 and there is no upper limit. The nohup command runs the command detached from the terminal, so that the terminal can be closed without killing the process. The character  &  is added after a command to make it run in the background without blocking the terminal or shell script from which it was started.
     
    To make a permanent process that persists after a reboot or new logon, put the following command into your startup list:   fotoxx-wallpaper -cycledesktop albumName 3600 &
    This example changes the desktop wallpaper image every hour. To change the wallpaper only for each new login, use a time value >1 day: 86400.
     
    Note: this function works only for Gnome and Ubuntu.
     
      

    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 [GoTo] button in the gallery view to show the bookmarks and select a location to go to. To assign new bookmarks, press the [GoTo] button 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, press the [GoTo] button. The list of bookmarks is shown. Click on an entry to go there.
     
     
     


    Sort Gallery
    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.
     
     
     


    Batch Menu
      

    Batch Convert Files
    This function is used to rename, convert, resize, upright and move multiple image files at once.


    Select Files
    Select files to convert from a gallery window (link).
    New Name
    Optional new name with # characters to be replaced with sequence numbers.
    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\B0/180\B0, upright it (status known from EXIF).
    sharpen
    Sharpen output images using the two supplied parameters

    If New Name contains the string "[oldname]", this is replaced with the original file name.
    Either "[oldname]" or # characters 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.

    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.

    Upload Photos to a Website
    Most photo websites have the ability to upload multiple image files from a single directory, using only a web browser. Use the above Fotoxx function to select, resize, and export image files to the desktop or any other directory. From there, use the photo website's native browser interface to upload the image files.

    Preserving File Sequence
    If you use a leading sequence number, 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

    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
    Select files from a gallery (link).
    Select the option to delete or move to trash.
    [Proceed]
     

    Batch RAW (DCraw)
    Batch RAW (Raw Therapee)

    These functions convert selected RAW image files to JPEG, PNG-8, PNG-16, TIFF-8 or TIFF-16 format, using the program DCraw or 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 remaining parameter options (white balance etc.) apply only to DCraw - they are not shown if Raw Therapee is used, because parameter settings for batch operation are not available (defaults are used). The default settings for DCraw generally work well and you probably will not need to change them. The parameters for DCraw are documented in the man page ($ man dcraw).

    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 data loss). This also needs more time to do the compression work.
      

    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 assign a file name for the script. The script dialog now exits. 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 [run]. You will be asked to select a script file to execute and a set of image files to process with the script. When you complete the image file selections, 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
    Adjust Brightness Dist. Reshape the brightness distribution
    Zonal Flatten Enhance dark areas with low contrast
    Tone Mapping Enhance local contrast where weak
    Resize Specify width/height or ratio (e.g. 1/2)
    Voodoo1 Automatic enhance
    Voodoo2 Automatic enhance
    Sharpen
    Sharpen fuzzy edges
    Blur Blur image
    Denoise
    Reduce image noise
    Shift Colors Tune colors
    Adjust RGB/CMY Tune colors
    Brightness Ramp Vary brightness across the image
    Color Mode Change B&W/color or positive/negative
     

    Burn Images to CD / DVD
    This function enables you to choose image files and burn them into a CD or DVD or BlueRay disc. When the function starts, an image gallery window is displayed from which you can select the image files to burn (link). When done, the list of image files is sent to Brasero to burn the disc. Note that the CD/DVD/BlueRay disc must be unformatted, and Brasero supports only one burn session on a disc. Leftover space on the disc cannot be used later to add more images.
     

    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.
      
      
     
    Batch and search functions in the Metadata menu are duplicated here in the Batch menu.
      
     
     


    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 menu bar World Maps button to change to 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 locations having corresponding geotagged images are marked with red dots. Click on a map location to get a gallery of images from the marked location. The window changes to the gallery view and the gallery appears. Press the maps button and click a new location for a new gallery report. When in map mode, a Map Functions menu is available with two functions: Choose Map allows you to choose any available map. You can install maps of your own (e.g. country, city, park ...). Instructions are below. All maps will show red dots where there are corresponding geotagged images. Map Search Range allows you to set a search distance when a map location is clicked. Images within this radius will be included in the gallery report. The default is 10 km.

    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 red dot: show gallery of images geotagged for that marked location
      +  left click NOT on a red dot: 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

    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 graphic image, a JPEG or PNG file. (Open Street Map offers a download tool, but I have not been able to make it work.) Screen capture works. You can use the Fotoxx Mashup tool to stitch many maps together to make a big 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 graphic image file into the directory /usr/share/fotoxx-maps/data. In this directory you will also find a text file: maps_index. Each map has a line in this file which contains the map file name and the latitude-longitude range of the map. Here is an example maps_index file:
       USA.jpg,      24.855,  49.17,  -124.99,  -59.766
       Europe.jpg,   30.89,   71.25,  -10.73,   39.71
    The entries are map file name, low and high latitude, low and high longitude. All values are separated by commas. Spacing does not matter. Fotoxx assumes a Mercator projection, but this is not significant for maps covering less than 100 km.

    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 values. You need accurate latitude and longitude data for the upper left and lower right corners of the completed map.
     
     
     

    Organizing Images for Efficient Searching
     
    The goal is to find all images for a given set of criteria, e.g. photos of a given person at a given place and time range, or all photos of a given person, or 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. These methods may be used in any combination (I use all of them). Keep in mind that if you want to user other programs for searching images, you need to pay attention to which methods are compatible. Fotoxx is standards-compliant to avoid lock-in, but some other applications use proprietary methods (e.g. for storing metadata).
     
    Fotoxx can search using the following image attributes: photo date, rating (0-5 stars), tags (keywords), geotags (location names and earth coordinates), file directories and names, and text appearing in captions or comments. These are explained in the Edit Metadata, Edit Geotags, and Search Images topics. In addition, any metadata can be searched, although not nearly as fast as the previously listed items, which are duplicated in a special index file for fast searching. The Index Image Files function establishes this index and updates it when new image files are added or their file locations are changed from outside Fotoxx.
     
    The following is an attempt to give an overview of the options and tradeoffs.
     
    Physical Organization
    Directory and file names can be used as a basic organization that will enable you to find images even if more elaborate organizations (tags, albums, captions) are not used. The highest physical organization should be by time. This will also naturally group photos together that were made for an event, travel location, etc. 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
    This very basic organization allows Fotoxx to find files by searching file names. In the above example, a search for "spitzbergen" or even "spitz" will produce all the images of Spitzbergen. The function Tools > Batch Convert lets you rename a whole batch of photos taken on one day at one location or event by selecting the files (link) and then specifying a template name like "08.20 Spitzbergen ##". The sequence numbers are automatic, replacing ##.
     
    Captions and Comments
    A simple method of organization is to use captions and comments (Metadata > Edit Metadata). These are arbitrary text strings that can be added to a series of images in rapid sequence:
        open the first 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 for. You can specify persons, location, topic, etc. for each image and then find them again quickly. With this method, you can search images using any words appearing in captions or comments, in combination with dates and file names. Dates are taken from the automatic EXIF data from the camera. For images without this, dates can be edited using the Edit Metadata function.
     
    Tags
    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 (along with date, rating, file name, caption, comments, location, others). 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. This list should be small enough (<500?) to be practical for visually locating tags to click on. In the random system, you simply create tags as needed while you tag your images, following no particular system and without categories. After you have entered the first few characers of a tag, existing tags that match these characters are shown in a list and you can click one to complete adding the tag. If there is no match, a new tag is created. In either case, recently used tags are 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-up the tagging process. There is also a batch function for adding the same tags to many images that you select by clicking thumbnails (link). 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 (if not, these tags are added to your list of defined tags).
     
    Geotags
    Use geotags to assign a city (or national park, etc.) and country, and optionally latitude / longitude to your images. This enables all images for a location to be quickly found (or those near a location if latitude / longitude is used). If you use a camera with a GPS receiver and automatic geotagging, then geotags are in 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 automatic city / country via GPS is that the names are chaotic and you may want to sanitize them (upper/lower case, with/without states or other political subdivisions, fix mixed languages, etc.). You can fix the mess with a little effort: search for the location you want to change (e.g. MÜNCHEN), then process the resulting images with the Batch Add Geotags function to change the location (e.g. Munich).
     
    Albums
    Another method of organization is to use albums. Choose a name for each album and assign any desired images to the album, using one click per image thumbnail (link). The images are not duplicated: the album is simply an ordered list of directory/file names. This method is independent of tags, captions, file names, etc. See Manage Albums. Albums can be selected by name and viewed directly as a gallery of thumbnails, sequentially in file view, or as a Slide Show.
     
    Summary
    The following table summarizes the options. Keep in mind that searching by date works with any of these methods, and you can combine the methods any way you wish, e.g. "directory and file names" together with "captions and comments".
     
    Method and Example
     Advantages
     Disadvantages
    Directory and File Names
    e.g. image files like this:
      /images/2012/Rome-12.jpg
    + simple and easy to use
    + fast implementation: batch move and
        rename files with added sequence numbers
    + gallery overview at each level of hierarchy
    + easy to navigate up / down hierarchy levels
    + no overview of available search terms
    + long file names required for multiple search
        categories (date, place, persons, events ...)
    + risk of inconsistent names, unreliable search
    Captions and Comments
    e.g. caption or comment like this:
       2012 Italy Rome Forum
    + simple and easy to use
    + flexible search using multiple categories
    + moderately fast implementation:
        think + write time, per image
    + no overview of available search terms
    + may end up with thousands of categories
    + risk of inconsistent names, unreliable search
    Managed Tags
    + e.g. rome, Italy, Susan
    + Point and click in a tag list
        to add tags to images
    + Tags have categories for faster
        visual location in the tag list
    + Recommend <500 tags
    + good overview of available tags to click
        (organized by persons, places, events ...)
    + easy creation of new tags
    + no inconsistent or redundant tags, no typos
    + therefore searching more reliable
    + requires careful planning of tags
    + slow to find and click tags if >500 tags
    + hard to revise tag naming system
        (batch tag add / delete / rename available)

    Random Tags
    + tags with no organizing system
    + large tag count >500

    + no planning needed
    + creating and entering tags is faster
        (point and click or type-in tags)
    + legacy tags can be kept unchanged
    + inconsistent tags (scenery, landscape)
    + redundant tags (Susan, Susi, Susy)
    + tags with typos (scenery, scenrey)
    + poor overview of existing tags
    + therefore searching less reliable
    Geotags
    e.g. location data like this:
       Rome Italy 41.89 N 12.48 E
    + cameras with GPS store data automatically
    + add locations in a few seconds per image
        (or batch add location to many images)
    + search location by clicking on a map image
    + search location and date-range by clicking
        on a report of available locations / dates
    + useful only for searching by location
    + chaotic location naming by cameras
        (revise using search and batch update)
    Albums
    e.g. album names like this:
      + best scenery
      + Italy 2012
    + make ad hoc albums using any criteria
    + images can be in multiple albums
        or multiple times within one album
    + time to locate and add images to an album
        (find and click gallery thumbnails)
    + images not searchable by other categories
    + not usable by other applications
     
     
     

    Translations
     
    Translation files for Fotoxx are found at  /usr/share/fotoxx/locales/translate-xx.po.gz
    This is the installed translation file for language code "xx". It 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 formatted "xx_XX" (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 commonly called ".po files" and have the file type ".po".
    A typical translation in a .po file looks like this:
      msgid "The file name is: %s \n"
      msgstr "Der Dateiname ist: %s \n"

    "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" indicates the end of a line when the message is displayed. In the translation, these codes must match the English codes 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  kornelix@posteo.de  so it can be included in Fotoxx releases.
    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.

    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/userguide-en.html
    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  [kornelix@posteo.de]  so it can be included in future releases.
     
     
     

    Recent Changes
    This section is provided to help you quickly review the changes without reading the whole manual.
    Fotoxx version numbers correspond to year and month of release.

    v.15.11 (this release)
    • Pattern: topic was revised for extended functionality (contrast parameter).
    • Adjust_HSL: topic was revised for extended functionality (target color selection).
    • Batch Convert: topic was revised for extended functionality (preserving input file names).
    • Spherical Projection: new function to make a spherical projection of an image.
    v.15.10
    v.15.09
    • Area Copy and Paste: this function was restored after being removed in 15.08. Save Area File and Open Area File can be used to do the same thing, but this method is faster and easier in the common case where an area is copied and pasted and does not need to be saved in a file for later usage.
    • User Options: the font and size used in menus and dialogs can be set by the user.
    • Calibrate Printer:  new tool to improve the color accuracy of printed images.
    v.15.08
    v.15.07
    • The Fotoxx UI had significant revisions and this user guide was revised accordingly.
    • Effects > Pencil Sketch: you can now choose the foreground and background colors.
    • Batch Delete/Trash: new function for batch trashing or deletion of image files.
    • Select Area dialog - buttons added to change line color more easily.
    • Gallery Navigation was simplified with clickable button per directory level.
    • User Options interface has minor improvements.
    • Undo/Redo button: middle click pops-up a list of edit steps you can go back to.
    • Batch Upright: new option to check the entire image database to fix rotated images.
    • Edit Metadata and Search Images: revised to better support thousands of unorganized tags.
    • Batch Rename Tags: new function to rename any number of tags and apply to all images.
    v.15.06
    v.15.05
    • Denoise Image: New function to measure and reduce noise with minimal loss of details.
    • Newest Images: Select EXIF date or file modification date to determine the newest image files.
    • Cycle Desktop Image: New function to cycle the Desktop background image using a Fotoxx album.
    v.15.04
    • Zonal Flatten: New Edit function to enhance detail in image areas with poor contrast.
    • Directed Blur: New Effects function to blur an area in a single direction.
    • Plugins: Support added for commands that do not edit an image file.
    • Voodoo Enhance: a second method was added based on Zonal Flatten.
    • User Options: an option was added to show or hide hidden directories in gallery view.
    v.15.03
    • Rotate 90 was renamed to Upright and the funtionality was extended.
    • "Named Collections" are now called "Albums". Menus, dialogs and the user guide were revised.
      The directory .../.fotoxx/collections  is renamed to  .../albums (nothing is lost).
    • Mashup has extensive changes for controlling overlay image transparency and warping.
    v.15.02
    • The startup parameter -nosync was changed to the more correct -noindex. 
      See the technical notes section below for command line parameters. 
    • A multi-row panorama capability was added using the Panorama Tools utilities.
    • User Options has a new parameter: Thumbnail size.
    • Slide Show has added flexibility for showing image captions and comments.
    v.15.01
    • The Plugins menu was moved from the left panel into the Edit menu, to conserve space.
    • Adding and revising plugin functions was made easier.
    • Denoise: explanation was added for the new dark areas functionality.
    • Gallery popup image: new functionality with F11 and Escape keys.
    • Magnify function: the user interface was made more flexible.
      
     
     

    Technical Notes

    Fotoxx Limitations
    image files
    Fotoxx has been tested with 156K image files and performace was good on a strong PC. 500K image files should be practical on a strong PC, but startup time may be 20+ seconds.
    image size The max. supported image width or height is 20,000 pixels (compile time constant).
    The maximum image size is 2 GB (about 170 megapixels).
    Images in memory are 3 floating point numbers per pixel representing 3 RGB colors.
    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 file.
    geotag cities   20,000 different cities / locations for geotagged images (compile time constant).
    gallery size
    practical limit
    Approx. 10,000 files in a directory or generated gallery (e.g. from an image search operation). The limit is the memory required for an internal thumbnail cache that enables gallery scrolling to work fast. A PC with more main memory can handle more. If the largest thumbnail size is used (512 pixels), the practical limit is reduced.
    Memory = (thumbnail size) x (thumbnail size) x 3 x 0.8 x (thumbnail count).
    10,000 thumbnails of size 256 pixels requires about 1.6 GB of main memory.
     
    Running out of memory
    Fotoxx can require a huge amount of main memory to edit a large image. A 20 megapixel image requires 720 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 (12 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 OS 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. Fotoxx monitors the amount of memory available in the top panel above the image window, and gives a  warning if memory is running low, but this is sometimes too late to avoid being killed.

    Running out of disk space - temporary files during an edit session
    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 240 MB file. If there are 10 edits in the undo / redo stack, the required disk space is 2.4 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 restarted or the computer is restarted.
     
    Additional programs required or recommended for Fotoxx
    Fotoxx requires the following libraries and programs to function at run time:
     xdg-utils open text or html files with user's preferred application
     exiftool (v. 8.60 or later) read and write image metadata (tags, comments, etc.) 
     dcraw batch convert RAW image files to tiff, jpeg, or png
     ufraw open a RAW file for editing using own GUI
     rawtherapee
    open a RAW file for editing using own GUI (slightly better results than ufraw)
     brasero burn a CD or DVD with selected images
     xgamma used for the Monitor Gamma function (adjust monitor gamma)
     fotoxx-maps
    makes several geographic maps available for reporting images by clicked location
     hugin
    the function PT Panorama uses the panorama tools utilities packaged with hugin
     
    Packages required for Fotoxx source build
    See the README file for instructions on compiling Fotoxx from source.
    In addition to the programs listed above, the following are also needed:
     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
    Note: package naming and contents are decisions made by each 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.
     
    zappcrash - backtrace dumps
    If Fotoxx has a fatal error (e.g. segment fault - invalid memory reference), 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 hopefully fix the error (contact). A description of what you did immediately before the crash would also be helpful.
     
    Command Line Options (long and short forms)
     /.../filename.jpg
    initial image directory or image file 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, fr ...)
     -menu "func name"  -m
    startup menu function - Fotoxx will start with this function active.
     -noindex

    disable image indexing (see below for bad implications).
     -cycledesktop ...

    use fotoxx to rotate desktop background image (see cycle desktop)
     
    -noindex option
    This command line parameter can be used at Fotoxx startup to disable the normal metadata index update. The startup will be faster, especially if your image library is large (>20,000 images) or your PC is slow. All image search functions and map functions will be disabled, since results could be wrong if the metadata index no longer matches actual image metadata. These functions will work again after a normal fotoxx startup is made and the metadata index is updated for any new or modified image files. Use this option only if you need to quickly look at a few images and do not intend editing or searching. Gallery pages may also be quite slow if they contain many image files that are new or were modified since the last index update. This is because the thumbnail files must be created as needed. Indexing is always required for an initial Fotoxx installation, but may be suppressed thereafter.
     
    Top Panel Status Information
    CPU 123%  MB 1234  2345x1234x8  3.45MB  56%  edits: 3  blocked  area active  dialog open
     CPU 123% current Fotoxx CPU load for all threads and processor cores
     MB 1234
    total free memory, including the file cache (which can be reallocated to apps)
     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 for user input is open and waiting
     busy [...         ]
    some long running functions show this simple progress bar
     
    Preview Mode

    Some edit functions use a reduced image size for a faster interactive response time. This reduced size 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 megapixels) is 7 times faster to process than a 14 megapixel image (typical digital camera). This method is used whenever the preview edits can be applied to the full-size image without visible impact (Trim/Rotate, Bend functions, brightness and color related functions). It cannot be used for some functions (e.g. sharpen, tone mapping) 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 compressed and saved on disk. In memory the size is (pixels x 12). A 10 megapixel image uses 120 megabytes in memory and typically < 2 megabytes on disk (compressed JPEG). When the edited image is saved, the correct file size is updated on the top panel.
     
    Ubuntu Unity Launcher
    The following launcher will have a right-click dropdown menu with three different startup options: blank window, last image viewed, and a gallery of recent images. Save the following text as a file named fotoxx.desktop, make it executable, and drag the file to the Unity left side launcher list.
     
      [Desktop Entry]
      Name=fotoxx
      GenericName=Photo Editor
      Comment=Edit photos and manage collections
      Categories=Photography;
      Type=Application
      Terminal=false
      MimeType=image/bmp;image/gif;image/tiff;image/jpeg;image/png;
      Exec=/usr/bin/fotoxx
      Icon=/usr/share/fotoxx/icons/fotoxx.png
      X-Ayatana-Desktop-Shortcuts=blank window;last image;recent images
     
      [blank window Shortcut Group]
      Name=blank window
      Exec=fotoxx -blank
      TargetEnvironment=Unity
     
      [last image Shortcut Group]
      Name=last image
      Exec=fotoxx -prev
      TargetEnvironment=Unity
     
      [recent images Shortcut Group]
      Name=recent images
      Exec=fotoxx -recent
      TargetEnvironment=Unity

    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, TIF / TIFF and BMP. 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 files named "index_001", "index_002", etc. contain the image file pathnames and those 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 usually adequate. One brightness step (averaging 1/256 or 0.4% of the entire range) is very hard to see. A greater color depth than 8-bits can be useful if a narrow 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 noise-free color), this problem can be reduced, even if the image is converted back to JPEG for final storage. Only the most expensive cameras produce RAW image files with more than 8 bits of noise-free color at normal light levels (ISO 100) (status 2015).

    The image below changes gradually from black to white. The color depth is 6 bits and the number of brightness levels is 64. Note that the brightness steps are barely visible.
     
     
    Noise Measurement
    The noise measurement tool (part of the menu Repair > Denoise) measures image noise. When used on a RAW image, it measures camera sensor noise. To work properly, the image spot being measured must be an absolutely uniform area, otherwise variations in subject brightness will be measured instead of noise. The measured spot should ideally be a mid-gray tone. A bright area may have little or no noise because the camera pixels are saturated. The measured noise value applies only for the ISO exposure level used. Higher ISO levels have more noise. You may be surprised at the typically high noise levels in RAW images. Cameras filter out this noise when making the JPEG image. Very expensive cameras with full-size sensors may have noise levels exceeding 0.5 on the scale 0-255 (i.e. noise in the 9th bit) and this is for normal ISO values around 100.

    Measurement method

    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). The image being measured is actually a 16-bit TIF image made from the RAW, preserving up to 16 bits of data if this much is available from the RAW image.
     
    Alignment Algorithm (HDR, HDF, Stack, Panorama)
    Relatively few high-contrast or "edge" pixels are selected to control alignment in HDR, HDF, Stack and Panorama. 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 the cylindrical image projection used for panoramas is only an approximation of what the camera lens actually does.

    Tone Mapping Algorithm
    The method used by Fotoxx is home-made. It is not as effective as Fattal in some cases, but close.
    However, it is fast and simple.

    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 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, bend / 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, color or contrast 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 the photos 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 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
    The following files reside in  /home/<user>/.fotoxx/
     /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 text files containing searchable metadata for all image files
     /mashup saved mashup project files
     /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
     /thumbnails thumbnail files (default location, user can change this)
     /write_text image text overlays saved from Add Text function
     bookmarks bookmark names and image file locations
     KB-shortcuts user-defined or modified keyboard shortcuts
     logfile Fotoxx outputs that may be relevant for diagnosing problems
     metadata_short_list
    metadata key names for Batch Add / Change Metadata
     mosaic_tiles
    binary file, compressed tiles from the Mosaic function
     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
     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
     stuck-pixels
    data saved from the Fix Stuck Pixels function
     tags_defined a list of all categories and tags currently used in all images
     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 or other programs. Images can be searched using these items or any other metadata as selection criteria. Those marked "index" can be searched very fast, others more slowly. Items not listed here are searchable but not indexed.
     
     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 image edits
      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 and country from camera GPS, or entered by user   yes
     EXIF  GPSLatitude, GPSLongitude  earth coordinates from camera GPS, or entered by user   yes

    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 ignores this request and places the dialog somewhere else.

    Benchmarks
    Conditions: Fotoxx 15.11, Ubuntu 15.10 64-bit, Intel Core i5 2.7 GHz, 7200 rpm disk, EXT4 file system.
       
     Benchmark Description
     Time
     First time index of 9114 image files
     5.2 min.
     Subsequent startup with no new image files
     < 1 sec.
     Subsequent startup, 100 new files to index
     6 secs.
     Find all images with tag "Rosi" (253 images)
     < 1 sec.
     Find all images with "Rosi" in file name (111 images)
     < 1 sec.
     Find all images dated Jan 1 - May 30, 2013 (255 images)
     < 1 sec.
     Find all images dated in 2013 and with city = "Freising" (90 images)
     < 1 sec.
     Find all images dated in 2013 with EXIF "make" = "panasonic" (342 images)
     3.8 secs.
     Click on world map, France / Esterel (5 images)
     < 1 sec.
     List all locations having images with geotags (6714 images)
     < 1 sec.
     Click on location Germany / Dresden (113 images)
     < 1 sec.
    • Initial index and thumbnail generation: 1752 / minute (2027 for SSD)
    • Incremental index for newly added image files: 1000 / minute
    • Search using criteria that is indexed: many thousands / second
    • Search using criteria that is not indexed: 90 / second
    Source Code
    The C++ source 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 you send me any images that work poorly, I can use these to try to improve Fotoxx. If there is a traceback dump (zappcrash) on the screen, 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 algorithms were adapted from this book: flatten brightness distribution, noise reduction (median smoothing, top hat), sharpen (unsharp mask, kuwahara), embossing.

    Acknowledgements
    The programs  libtiff, libpng, liblcms, dcraw and exiftool have helped Fotoxx evolve much faster than otherwise possible. Of course this also applies to GTK, the GNU tools and libraries, and the entire GNU / Linux ecosystem. Thanks to those who have donated their work for translations, their ideas for development and their time for testing. Special thanks to the following: Dick, Jill, Jamps, Andre, Doriano, Curley, Jan, Rosi. Translation credits are in Help > About.

    fotoxx-15.11.1/data/images/0000755000175000017500000000000012616075370014034 5ustar micomicofotoxx-15.11.1/data/images/remove_dust.jpg0000644000175000017500000002777712616075370017116 0ustar micomicoJFIFtExifMM*JR(1Zgnome-screenshotBPhotoshop 3.08BIM&resize sharp resize sharp 4http://ns.adobe.com/xap/1.0/ 170 265 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?=O iRIc35d1$99IbKlFҾf0v|"Gx[赪:3inַyBZ+v2xt Os5Z6[?ϵE}v0w9?:G m.-O,_{hryJҚdM73~ţнMbѿ^? ?&uG}F{H(>ţнM&7Q^BF{H(>ǣлM7uQBFwH(>ǣлM&M{(i.s= %bTy.HUҴg$Gx} <{bjQ]]imo,'}5-qUk#warse,A\qZ喒i#x٤,t](QcM&BQ5< jl_b ZL22m=>b9>Otot? GGָ;qY%gJv=KfZc}GwH(O.ZbM8C3]`푊W2 ;1{p 敯rYiJ2|5lcif]? ?& d[٭ˆ%vȶaiTKCU no%v ˹-txsH8Im#ku9````;g'*K).ϣлMg]? ?Rn0Or_.R}GwG(£F=; K'лGйQnu?c%ܓ\ ??HЮyd4?Ok+um:mҲN146f-߇4$ L Ek{q+>ҵFд 8t#$1Q" IwQEWcOxgJڻllnXU$ tG>WqFu?kǪץ sUM%8v_n櫓'Z<~|UY]ǧ]tIfHN[i823ޚm;4]Wz"ձ6E}-ҥͶ7`Fx=$.!m9bw)oBSjϹ="}ժ!%5ZkV 95Ʊbo`nfع {ϸ{8v3w@ _Z>u@ _ZЗŚjPv 0 q-:8YHcdL"`~`Ih?]Wz]WzXf`ƨQApyeU*7k SxwUcgkPSxwUSf?hh7j?t5UHVC_?v+4w;v'ҭc=}a#zc}'MnBdd}}հep˱8 wpGc֫EYK rwln2:Pϸ#ءKjR}ե/j>e?mGqrG;jQ;jV,eO}}Ò=߱WzWze?mG,f:}:ե/j>e?mOOrC;R}:է/j>e?mGpc3W??W???YQ/j=NgnO=WlgO lZ_?b9n;Ȇ6, 㯱%fƣ\q$7:e݌k$LT n׮:u#4_ѐW?/?UkFomnlXxPwu8[?3btԆrVn.iysqeo5 O/!و.[X}ѡ۹sׁS]e >_jT1N,$1 u:͸58.E@nnDhA3`uwXѝ*(3=iA9̯:j  ؼi]0$t4qך}ng_ZLBD݁I?-o~&K&t`>єK/8Cަ{oGip. 62Ÿgr6v}cnd?5bhfY|֑!Ћd?MKeUET`RGMK禥|ֽj?Rbh15EdyZԿF+)n?:@S@,7Ҥb,fNsǭS*GVfk+0Ddw]93e ĤdF_ I}tw0[prdux#U|.ҳ2V|;u #"6wsm ZsMD wmb9翽[c[ľYorぞ"VK^8-g?(_}m [NK~bD"!3F _kxNe#h#n%c&vaq5-nu鴿\ H"LPo v!{I?cgwoի7ߧ!FX`n9lZ:gw F̓ v`;[vrR@ll2;_]ߣO̴/Gefgu~ڱϵ4ORTfZvo j>_]ߣO̴/Gefgu~ڱϵ4ORTfZvo j>_]ߣO̴/Gefgu~ڱϵ4ORTfZvo j>_IvȎk]j?ORU4+,*T1vrONMc?3E`|_O?3E`|LG ؼi]5s: ؼi])i mъAaIoz^jWHZ[T 5vm;P[)-n|pGV]E}>m?b<ȇx| ˟.E|usO#;F V*1?Τ0k*uC$E ###_أv)˪ho9/Sl%C(?5{ȇx| b3ݱ8&5bi^HtV|t 9!h((((((((f2 f2 yҺS5ҞE!ψRIjWx-QlPIn}WGFԥfk f<kNwmoA` 'O񦘚[&ri# Sy(&":bݛj:mng1¶Win@>w>>w>AbKcybۘn2vu-QK,>JŜ$c[ S)?Z$h<@$tҁ>wn [2eF@` I@OoV6S#G4SrNIvh ?h ?,nbJa5-uH-䶷Mڗ}2rq~_Jӿ~xzX>&?Mk A($SlIX(0(((((((( Q/]+ Q/]+ b>xgJ高Ѐ3xdt\~gi u.??34yq1QMc?Gu.??34yq1QMc?Gu.??34yq1QMc?Gu.??34yq1QMc?Gu.??34yq1QMc?Gu.??34yq1QMc?Gu.??34yq1QMc?Gu.??34yq1 MGFhw +@X|H+ b>xgJ髙xgJ (((((((((;ٮZw sw6RU}+à8Y |oG=K`5ΣVpFrxGggO/GM<TUp\iJܲp,ފל:u-/,Ls,C4+$2A9 =6vRۼ$Cp;8C*s5i{y%ԑVVFܠv3($uk]+Gz;vJQBJ#׽Xɳ N:p&ѓ sYgRt!n4ZFAubrVW'hlɳ &tsGy ]\H WV@^v9⯏Ouu0[Z4:~^[;x4\W]],8h Ʈ>l5YGj"DUW =M>P$[9'IiKH ;}*?;Jă_֎HFh"HT.Ыn@ t,Hh՞.#cnIO^}M sȩj%X/?>9Erˊ'RJ?J2Maj?3E`|_O?3E`|LGҺeCNc xn0 >q0:֏_3,i_ .g)#5S!J%^9?Ŀ ?g/#_?)ƏH j@J%^9?Ŀ ?g/#_?)ƏH j@J%^9?Ŀ ?g/#_?)ƏH j@J%^9?Ŀ ?g/#_?)ƏH j@J%^9?Ŀ ?g/#_?)ƏH j@J%^9?Ŀ ?g/#_?)ƏH j@J%^9?Ŀ ?g/#_?)ƏH j@J%^9?Ŀ ?g/#_?)ƏH j@J%^9?Ŀ ?g/#_?)ƏH j@J%^9?Ŀ ?g/#_?)ƏH j@J%^9?Ŀ ?g/#_?)ƏH j@J%^9?Ŀ ?g/#_?)ƏH j@J%^9?Ŀ ?g/#_?)ƏH j@J%^9?Ŀ ?g/#_?)ƏH j@J%^9?Ŀ ?g/#_?)ƏH j@FMV]BMGP"KkvwF_0֏$Z b?fotoxx-15.11.1/data/images/sphere2.jpg0000664000175000017500000007575012616075370016126 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-15.11.1/data/images/brightness steps.png0000664000175000017500000000231012616075370020027 0ustar micomicoPNG  IHDRFL "IDATx^1n@DAl&،iGRUIFdG9cPbg%7GzTXtRaw 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-15.11.1/data/images/sketch.jpg0000664000175000017500000020121212616075370016017 0ustar micomicoJFIFHHrExifMM*bj(1ri%@HHgnome-screenshot0230ґZ01002015:05:31 15:13:30Fotoxx:resize|zonal-flatten|tonemap|tonemap| Fotoxx:resize|zonal-flatten|tonemap|tonemap|NE9http://ns.adobe.com/xap/1.0/ 313 877 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?(s \R v} *>#_i~ [- Y8Zھ)PrI!G'󟉚4$MYDեeBϾy*hSu&vc*RIe>4y籱I(ǸXa$#)Hofkk!X(n0`\Ri_vg9xQ6ӓ=q#+XX5y=a~.4a4G[n r5xdzk%@)J;nv9LHVmAۜg'Gp:bɓw.8<O_*x t2A[8U$w 7AhLd q8_,a^G Nu#jWn@ӝ\|/޻5D4%G kpF9ڰJC=G+͒ϧ^<!MJBh4h&cJơC]Q42<Ƥ\TOcHC]ƘCtT).~ r?xѶgiDH0R ܸރqRZM, *GDžcHaֺX'h gx6qnVtw`Z6<{n4$J ]#Sp9VSra 0oFf MrҴkAcoZe=c wf^-N,졢' MqMv{Xz .7y "I4ԍ-yl-3 pq Yw|DEG܌OW+}Mu-Gٵc;F;؁]FX4G[{Ibb\(#EMQ]"9$XFSnڜ\,v;.$8~ 1]WמmUW rR²uPOSsqƑ7}^ys k2e y $#wm3Z'W 80:ZW0_ݫ.zU]R-6[c]% ڣ׃T|OK/$jYH$-EĞ\2ʺsj+gjvQ7FGfrFsɬ# R%AtQXo.6y$|^OQ[Ww7.uoMm U3 <`֍7JԢi/Bm?xXF,PMkfYdK ~vn|g\ɫr9AZId$@Cx" @RwxsSQGoX3Nt'BTf5KxOP \gcS ? fhu')}HkLՑ^\VZ%۬V>kCqs[;Ӓ6OfI RTiUUڊ2N`d֥az1Pw E)((U,@$Yz,03-̒AؒOҵT"A 3m1:J3c>$si{a׋2ʫ1sFv^}=v[!Ԟ(2#7!x.r1ө4kmkwש,V+vw`QAK&|[qӾ+;fEwuN G~sӆ)'(8ɱ*x}?2fۀǿ$x'ʀ*>Hʤ9wž8$*V]rK!{8JW#<ȇ T<?z( ѷ,G =:w!19 'v}G5xnP$6U4sYΤc794Ȯُ;`c:ƫɰ92zv6FU22_tPz-1ުj2ٸG(`K3ܝҒcs>Y@]j={?Zs!*7 r01aJP9?gޤp0AZIߔ'#8*7]o1~}*@6m`YTGQG=sx@rl')!b~iw?ґyRbA~"CRBÝ̌Rq^_$d)-[ /!1^f>~b9F68e*N=3jxVg~<4ϫAI|LNA¼#12;o*e[dl=BڽDլ||7u Qz}1K;TLjċUd=E$]֋5HɆ'Ϟ=n]v+" UU4څ]Z腇T Ve_\zr?*+B{%m.y@N;{]$q`01mmss,ֳI 4SFv:੭8]j[a2X622#sOn2BF#CߓSi% Pi -TmZ$_\l3E:٪+˝=1גE`{ m"KhtXo>s:EKZ<+=="n/G<^W;F|pΓ[iֶ˧-Rw½sܗizG4]\I5 d@RA]ܒ+-O-G-J^"v1WP~eokoqvB JWܻWkVVg68Kob+x͸ 3!JZf ʷp]]X qǭG6v5H9v(MW'r sHkKJVwL&[ Kae cEDL0THEqabg3O-˗!yO$ /K$i&ln-I"\+d.@^^ ӭh zln[ gtj-`S:ɧ=\I,w6\RsZ]ElZN-tfe?TPᓓ9dC|i K(_$r~ ,q+7Hf:Kݕ5¢ \Ypܜufw=>t7 !4cVcI ŕ C,$Kg 7RiwZ\"ivK"*c)8T9iw@+K28!\b?;x jx./JT,HTՎ@psUc3zk]C2ʜG5(<(pQri˧?bfHx˻s7Ȝ21\v4V[u 应F?ao/xp.p,jmbmJ.knm` %RBF~Sx=_]-o.㌨.1'X4K8Xr#MsdhKN߻\ߧHH:5ɯ$ e}>X~VVY 8#v<[l>j[e` Fx;#Hŷ1b۠Il|yr/aMԮHGpSz $dq䞼 ZR  HN=:RnWc(O{SS{~`8<=w9C2h݇cCjI #99pt<9%bª'f">w=ҬiS9[lbdx8XF\u1=\> fͤګ]&1Q+1ldSc8 imjmBN%^sDi5|Ze8R+F : \. Dtq cecMCE;c|9+5ZPlɃ08_z`^Gq Mr:0ea(´?1__O jp~ GQA>|8 > 0"}pM ||G5~5/sʝҀ>m@/?'+Ot^?ZzQ`>it𮣞]iO+_te(zP៏O jyi᷏ב]C' 5.[Ҍ|17O)g Q' M@J-F[Ҁ>is0?<+}͖-@ ²MC/Ӈ_*?}͖-@ µB/  _r(zP_~ ЫK4µB/ܤVEO)]$?V?U%?Z@WQk혯Mo%Kr ppT~ucj,~ ЫK4µB/>kzQ淥|=  Sixb-OF YȰʖ0w1 v(U=pAtӞsAO&Pz^u5tࢬTslLhA{u=iA'sim $sڡ@sZ7#q?:ӵɷlFS9'd\ 2(" A)ĺu"[Q$$ dcipzVʎ->y$t3[k km$0}6I.걧C8="њ+h]X3Z-w*D(YIU[Ve{mobǦ< Ʊ{gѥJ[^%Ĭʈ%v䓜5 :sZ`wy#;G̥~J/ͫxQF-VHPH?)}՝(>OJ:^4J\.Xǹs/9c@7g6,v6yĤgTrq8|" D7QBH'Þ½-bT\m<5XO' ̿?mWcѴ.-Hv&DVPvLL3k,`~ K<_gi^QF|Oe=9jc:1n% *p oHIWWK6n0'7̠9S>^'(A smuKcyv(I#R61c g5歲8fF1d} sn_C!H!-EVRÆ@p <֓^KXEwvhc$;Rx\1|m=+ 8ELrm2 1۸HT4KF3ߡWmye]C1+DZ޳`-Uy݁^kՋs,F̮x_8GAH y'u<">YqvUMm# M`ǯ_cTHpJ}Һ+P0?ArҤn9qq:Ue`S8.@mC$1szqYvZZ7usVO/ rï\c+~ʊW*N@=Gls]?m#]4) כT{mySvK>HTvq ?h}s%H=CG)-^9U8j(HT`ڼZm]RGxCE= `k^% k}Qemn-X#4 θӽ{wy^赭O~y'gstyޅ65K7[ѓ\BM K*@F} o>mUnKn1n9^㼩=qk6 'N$⾔%B#X _ ? xW6XYZvWm]$#R Og9SO?,R~C&5BEF=Mjv>?E.cۆic:\${sZmimyzO5f5^aJd~P+yrS\#Tx;mFws[^5.G+%+%צuwx}f]܏2W?K_9/f7u.=̻/c3G"r_҂h 2y"r_Mj au nUrI\^Nkɨe :Qwȝg5fek}G\%2y̭o)'i'Ӥֳ˴c\OrOR}M?-'t:M&MUo-O8 _:|yf>˩~l-!+h2˻3}3w u<|`Aol\bIsj DEMlncK$/TܙіFS<8 WGƚEu<(nlA3X9 ,z mb -nt62FR%\!1X  I8Tel/6\42XdeJ^I=ui̤mlg }.{xM'vv_0C(3&$ۑ<Gyeuom$!nn/^725,Qs,H1 ` ڱg쥳BV@1,C9Gi uطx%RpX dqi.5{9b$IPdn wA߄ƧekK͖]+˼Q\OkjDK o o(%1syk|!:x Q#!#iG[%6fX2A#z;9c.y-| R*QjkogD$QA+\?%H Ϙ( ?Z+y/OY^Մ@SO9jrj of $YBۜGM\ n5spڴ[]^[oD*ʱ1Trpy냑J͔=ΉrWs%~R0Nyϭmjf{*dZ240[r ڥ3栿Xi+w+0[@g'8OU}J=a[S8Ft8= -Wy}]XQ#Ā(i7 b0z޲kel52 $n8^Ur]A4 .HDnUr1`C_Q2jȴ0Y&+In8q?c)}hBLn#B@8ZIKVSr"2 c888oWRйGd6H®Jdd'aUI/4b^̺t&Dp싽d?v4}Yc{${7qn '8| }7SO($h~7$BoR/&E4!Ob@1[$7ZZ4zv;C:{Idf0dXd<ˤ4:R~GG<ǽ"ːW @mu{)-oU~אsh.H$9R #) ؼC{<ږyeգʬX|)ʍ vrϧY^I .[q N ,! uV2^K[XkQy"H<6pNW5K~!,U+01F۟blڹ&;?:Fim/Nff1 xsқ,qhJ>s홣V `/I.f+7FBRwcrmRjo)խ@y":q'v"< ;w;|1NJou沷fXBTP2q9^6$;w` {aLzA9<)?Łà隆#Im8T$BGA3_GNi#'^Nsz(-=J`rGnEUFwuPUpUᑖ s&p1^y~}*q)!5?bԣs:q$|" }+ɗocag_eRM7׶E 7W2BΈua@:㞕z ehmܪXaLa@v8r89Z՛#yJ ~~vs\WܤO 1$UEbR?0} ]`N 좹 qOWjSдN该^Ioq,{[Zvc0rI5s)mC,} z<4֑ Gpr@F1j;%&RS{^55v [j:L-%%0Bd̊8+?yn+Ww\@Mm߾ӸnϏkdžh6ˁMm2f> Gz=}}xbG>#Αbڠ8یE[^w< +pp8=ZM,qp8xWv'pv[ U݌{C ִ!Mko/ג'2‚3 tOG:}dim.c!3,.w O\Wsoեjyֻܿm&p̱#Hv 3?VG~J0d]XmNTyg-?:@9^?柕iV|}M}l ?Ο@S m4OʎJ̾!(#S gONtWoyF~TrUeiC[:uO)l ?VOʍ4҇??tQ gONz~ToyG%__w?)'T?r[zrtS9^?柕53ǔtQW=†_mk{򨹍Y7 :g5q^jb8')Ul|حu ye:dDķ>Gǫ.:xYoY#uv<֭WG%2Q < o}s@9G$2WAѾ9Ie?^pF@tR=A oq Lm>XEo 6$b-ģ Q}Kɯ=CѸ(OŴ"yJz ";Sy{U Wc|Gw|.=1\lyayr-'J<(&<]5yydV 5Iӑ59K 8qk/[ [52jזC,2\0yVNPXybTKLZ4Vexܨ';rG5MM=76o-hc!\mWsf#GYqtȅ:2.<8Ö8v:g BzIm2yi,XTtA?)Ԯ#h@@$PUA g''<3V̂IH?l0"#=O<Ҟ1Ǹ=ZyoԓV`V5@Ywd@$Sӣ\ $%2#"S8ن63w CQ]ZH-@th#n2K|r}6=*U6\C!}+<q.b0qrʓ=b8m,%ƳoAx "{i;* nwz}Ưi&#b 9p,hJ!J60>\kZ{(-idmۈe`׊y$6K(iB\A"@X,Eʔ%()-o|Z Uҭo7-A 偆FThrw`k:d3_]Yd7 ;3Mn$a(Q]+wqH9TA`~ґXno1$Rw$K.`+rI; &n)%Uޯ~] T ւ@ZkE Et7+!.aܡ<{2+g&0pΤЦՔ3e{--629r:Ɍqfhm+7FppԴ~pڣ)ez8v-P+ #ncMfJQ~$o;i]$VfqQczVNdRF]!!P s5[kk|^ ONL|͜w<qS;{ գvg9^Q>bG40[ZBŶK1?#Q^ebfl׶SG,! mb짌/یW+iBh'y-$)i"QX'xk۫fX--dUK:)(b6nA>WG\]\_\E}5cQo,L흪U1~Hjw}ťcD!$ƃYp95ZAcf.V3 j\6ѐpIʐ1Ҷ sZt Ȅ6 W.I$Q=^j_,vt88.n7>mrQѮ b3zVgu)o[^ZIJ.q*}xOLZ;ȌEekIyymdWlx rI2kthH6&IF\7!@1=pEo 3SǤnE!~*5a;:^J=)i滞{)Z[9FM嗕8>)ɬ&]^;t1Cɤ%Y`vl;q]k{˩,'xaX$+! w(NI#G/hc9湿ƪ݌֊)mc6 vg܎pK=^9I<:ʶF?x`U o<6 ,dtk#K|θv1U$qiz= `G, z1޹⭡9])mu1^-]_ӈ$F r}p Z1Kq[[*3x$r0G"gK*-ԧ9<ōA`\#l6Ow.ge$eH%)M@5>Rr'zk"ȍe# W Hմ}>8b$r[. Ң{s}shOĢe,[ dUoy=ĞmW ɹҿ `A *Sѝz|D,3u(d3sa7Bx&ăўx:oh^! 6Y, Xœ0\08^M7}{zjpT83/" 6@$A8M/y3LFS0$Bn0}+ป@@1Սm+Y!f ڻh#׃WݥHuu8+G6:p$>60ɓSG9W|jx4xNQu#sWu] e\(<EKHt|DeI־jy?BX q{7~ 3I[W1)75,u4f2*nKtT]Bx;@7Q`1k\-Śt?V[HBA 0;^CFvwbf#}kĥO5w*I#9>"t.QԭZH$ا!s^l uSM6)/.7=X,r|*Ӡ桴Sso:|gMo4\.[}cH%MNd=(, L⤏RfYFDfVIԄp6~M{D5;ct"S"P\e/R:Dsa%BwwϨ<J6WvjKe9akpC+ȡw"rsq3A"VU7N@33|%I~KckOA;k:J?Z<^ [H–?'w|.z0UӎASoލfu!N*ߥ&cUA h$t=9k5Kak=If5qay# KGNqDV'EJ<\"Z_(KK:t^Y>%??DPEJ<\"z_(OK:tq݃?iXnsr2>c!SW>o;Qɀ@G¾a*Ec'e v:DeZ܎淀nZ-M]$m'G_2Mށ:Z܍ctW0N.bA1)d%g'!WU80pnNE"%9m,rG8T| zM=k, %@Ϙ(zv6 &#[_Fn"gl*Tm3ۯR޻A!Աc{@-,q**x">0\HDmM0Uۀz)=yv$  HG 8^-ltH.,†U݀@!8צ)$y%*Я:ɸبS-(c$6FԌt{O𖱩DuTqP(- im9.4\ebMy ݃O$ —[Yn-V.SHi@%7dCs aF?mτmVkPB$K0`B`|qT5uDQRL$2#tEylݎO$E:Tkc֮+xLn#6вx>g;r86z:\X%%]|ʓvsnKٷhR"JυAk;'2l~ 6~,q5ߟ\kg"gcUMSk3J}Z!hk!f,UT acNI[R`ڕ_ k^]=cq] ql gh$v:kow r+ {Jn&Kx?xJm a%+Юc6BL'B}A{}ExU.}~ ҡk x\SUe_g9;E.&rc7WX 9Dqozo(c ";'0`q!BX<sߟh^CTkbL'߂+ҤJqrM*&c"40?],<8VtE~}a%!-AʔE*sך=tL #)+ 6/ c,"B@g }lF^ՠ ``3|'.`F0x}sQ@` 㨫ww6YZ)0 #Zaӳ5t.3(ۀ遁=;UTex@9ש'i3'qf #q0@9ޏ:NBFTt1ӟU131c7`zz#19+ 3`0ѶYO&'hِ S Mc\xSVgЕ_2f# 29_|/m~ڥODU!aۀӊZ0ǂթV`s֮!5} [M|`?LmzVifTLhVGY \|Ğ蚥6Z] LqFH G;>ҎZf%j$!7F!޼`¹Ѿiʐu+ Q9zc'cYMQ-% kg8e%R!|۸=1^y FR\+)<ۻ 5u$# 98U.oya{ b^ma(m,ܜGOBM._ x7]\y$,>G|e5Be5G2m?>>Giw)VP#5H2M~$Йo|MGVuӮn`Uc{.sykjJM2dqw+Vƿsx֟$Dbr8Ws+9C|0ko~$>߯ϝj0k߉?Əm~$/o~$Q}ϥh~.?/"yImi'Տo_Ui x k^3HJ.oԎOц"0 V=U!X P`޾`݋ȥ"ţ޵ɘ:*,3)8 xcCM-GO mRyֻ7FNU(bffS u$r$ Gl)jyНZb%""_s 89mXj ,2A4ked33*F!cm ʣ8Ga6*G (P. wm?Co1NK%G+\ 5Nu R(I^b^+Z~=k"s6F!2\99ߠ?+$N:OAtNmuLFkxR$$ ˒WHW|QeHd-"*]KAqkζ9gFg@Ǐ ont{e~̀1#+Ni˝eE1AbVo3>T1&q hy"GeY8yQTu*ry2ۣ+U]K󓎧?\ Q ZOLܴ;dɓr 5aCcy$~Iםd*Wdcs8 8=K=2.%т- *Ĺ#p'jRtBiGen4fiKtĠۃw9߭qE4h092zEyu!)]˖DD Cu<4Z=5d)-$MO͔I+'yS /-(緂mֵJK?@pAcjP.^ R8F`QHִWzu4wnt>e`b^YX|vRv;Gw}r--{cԟ:A>ޣL9*AnN}=127\5sG#G`Nc$?Oz 1 ;qSW.p,ӀXp}gSTkGI&z 7I!$',F{$aPG$S`w5,}޵~ʻ!d24gn2#h͕GEcA@~4/`KqRt,a yecpDmH\Zne-@fkC+<ܣ2oWKSn.[,䶂"B%"2Ɏ2l ֞$O3=$̒K<t}i{ukt mB1ӀåsswIY/n];9mWc&PhVIU! ևG[o6AyK0iLLђ;?v7 u;k g p[t۴8QՕxNI7kJC'6 <Z[9<3x&'1F(b-!;`r Zwd!Xdb$wsAHS9 23~GoR&Kp bq}si@?>s p~G?إ[^?N\$ƬjPҡԬ'[[gnKJ!#!XS⻲"'p%RSJ$lpj.!AsyrHVc52بBSU;!#3z׎_[T>mpB38Ǹ[ KY.nf(eӫ fhN嚱7޴y\ͷ|=ujhIQ;T':+SB-5Ue2\\]3ڴ14oZgGs^KO: 4淭'޵cw,]Vܮ)UW>$Ѽ?_PIRrT7sz+@g@gA!s.џs]1]hO9h37CEWlFW#O,,I_+q*h++ܮgk ѸRS$vFMƍƛK@~a_)~D>,2q(T99 ۧJ/+^̀vi0$g-۠/#l%K+6I$1K`'On%^lH*b)T^1xx[]iFea 3n$ 8bGt0EKiSoXY%1!v1 '@Ms|Ӣ8Lq&@1seB]ⴚЋn<7;w2mP1Ԟ >g6&&`S!.$)S?M+N2ĶzE)_!X-է_u Eurпmy.Jù0 c$Ժ>\_mh`l/:9*啂#!$@%NZڎ5fckwd²Hr@\UK{i"Fi\}㌃CRsrr1GmhW~ehnd7"IwmpdJ´P~b_Y+ (Xo8.@ X֌wiYsn#b'WtT5(BE0đ[o%H@xoȃ' ΤZ@ɧ]ZŦ"cu{N'8qqZK{[ٮc$3d>p#cǢXH-C7%f=D;v9ܜ{mYkK5nb!nn dHpU\ڞJI9oZ8hr0 wCKoό?J/U纉ch*L69q=qXi5k:ʈ2>G\SId֦8m. ΞyhJn$w9{6LlN3=Oy4J,^<. ؒCt^i^M{\[R s%LQZfy25 +lR{(GUg8,=94Z\ȲjV/90Jg>r)1 sp09s;J l(@ ;RfimAS<iVV.&e#q8p*@%٥ƒh۔b+V(#zդi@YV _vǦ~:߉R;mJKTE%BQԞ?|;=H2RHq4tO2e9Gls.dX#5rυ31!O@ hL8Hm`Qd۴#npƤ8l3@VC;eE oqzl;H HQssNs =8.'c}Jl!>zƽ@d?k+8-#1e۾}HFё1֚ [͎HpMM" ʠv$8 NJl\tg}˫y1Iw,˴Br22pkV%ԦRY6 G(Ȋ<9#\ςdwXZR]ͷ ݴ'@[bNk-`ǡWMOOLwNĬe;INFp:trqZiy$%?2F09P}F9$^+;*)S $=E.[?h=V!VG!/!!P(-o0wvzKE/\oGΝ*eP#_]hpgX2!pIP:.j6{xumN}6 -Ȑ62?{r:tyufܚj޽ o_\[$SJݸ$uIףj:uŏښ$#VAWf ǰ'@\'Su;O1 T!/ * 䪞Ϩ0x).]ifU l`:"#ҭ06d[=W wg=w2xw ޜukislszW+&ū-Oz=Ə> $Ƒix5ojZծE;]5r$pjw!#94_ h9)]_w>uc|U;HgP-$Y*F?:{Y8u} $ˢ} )vmT{aN= t(Rh^0̈́P]͒\'>jRH_THf"H> AXsAb~\V`WD*wĬHc cf9G WD`]Eٮ=f/%v˝d㚨CmymMҕqs" .G[g$ԗW "jQ_NnFm"~Q) !$!kMMڣB̝>nV$$rq8$]<ֱuRI-.WG$GL썆A݁ WviS-Wa)Q=x9EI%זmhB"\HUN9UNٍ|B4ۧ߹.m>ZA <[iksFd71G K?쇘 c;Q2Xn'ey =ŴK LI3\]K*Ü@FH=M$Vt"BJJNIP;rMm(_Ce KtmeA#8,rꑯlc禖 $[ߴdFQlx*nnq.isn,6pSm͌Eqb"-NKk&[.CL\X λq-qUg%_q۲ZD\>p2zW'͢C~Y>%FT(c")w˃ӯJ4(2N 3#sn)ILu:o\]ޓmn==e__:2b"ڑ~G]ޠ>h[Vt[=Hi,qxʀJ`VsjSjp$2mpegAm3XGGX,&vѝmǔ*sS;{6vEVh=6تrNΦ s $/wl iwc?wWTRHttZN6ƠeYFgKQ2Tv2ax(Qp=Ssa#3t!0[$ cpk$YE:r-q}yZ|f(0@=ҽ!Hbyf`ƥُ@5|M>èYO2iQ"Z $r`3ִ9fNDf)EQEٗgMǾLZ:>k& w>99#:g!;=101ytA8tZ7G8@B8a,R8IIFsl)w)gw= H H=w}߯֜l2* TzÞԳHwYThٶ!܌pqmkҗSukkq [*Ez^|:I9wO0CgCvFZKQbs%b0Iۀ `{WC2c$y-q\ZGrQYnG$*Xcn0qҴ4Oj7}hS풺/AwxI?tKmٯ--5>EnQ$>CF!ʣ#9'#hjq,pRT6֫igBojw kϒvtk#mM[{6䛮B+'h# ax]{{.fKÉpdL#la婶;KTˆ X' ]JTm#3m-mgUx|`λ 6RHn-q^4l,ϒ&! p=kMJ{1D8cP۷yמ֬j-F֌s:Y ;:(Y2OGME} &`{w[Yl݀y[u<9=?C^#o_QKiuWDgvxev>`:u:vn' 'eHۻ=& \8 A4M/}w|C<溭C֟Х;goKE0, %~Sf5sTtܔa㉌!&~=7-ݝTJep%`Kmfldtݚ9\.q|vO mŒ`c5o^}^K{T/wD]gI7hsT/WE9z@*AQfz&޶7 m,"J*uU~\]2\ 0!Hvf7\rf 3^i'&Eh͒015n^[{ǹUDeVBH[Ͼ{uGT|m=%4;L's> L089?֧(AfXZ=yr;QJXrөr0?GNA'88nJlH;('z\md A8 Sں*0xQaۡ/s{ߧy߁G ´iYA$ե2]J+ /M*Ѥg(I&8k5 /Mj"n(~C޾=ϭ[#ʜ#]u KFw)8XCv3P5$J*̥=qz (yZ-+RMſ,Fڔ^Xtd`3ۮ,e8 D<F;W,|ZMrwpM Ik68!_L=N >Kti":=:hmNFo($vң=ȼ x#v\H-/#s J6M(8q=>7><[ZH cb{P?My7\ƬMҾQkuݟ'S7Fm'ײ=lP?MnxNixdZI2ySڥ  t7jХJS'՚sJ*z6# 0U!9 `0*W9OoSc >PP 'Zmuir[vJ CFx{Ui-%UU$8~CՍ 6ʬ]όE>Z;G&R&B,z+ƴ񱲻/&L"1L*MBQ|C]J_+*Rn ]~l; '&?^#kx:5޸>:Kˋy-@H?{ auuwyfPaCώ3اIv%{CI=erMo4m1$_4f`i8 <[x绞m}ZG( L _ZiG.srH#HC;t_;Qa72%HS`5>2FI{}v&X6@,7W>(pC}k?t23U_ kd"фGO A6ߵ r)W*d;*)2S<5;lmE[j9X%&r=>>(xI^Npw%E J4~#3ž"D_8#"1zA[FVIRRD<#hSISGhBbHӣQ&,H.'UD0R~aqA^ױaM{xhBƠlDOm8gX_ `܌WI̳:q`md%ln}3#xjArw3TλL!95^j6߇$wŽŶ"UJ.kM&yU`SlIq`9YۣRTSB$KH#kfڠ)Ĩ\&Hoc L2d4jXIn#נ=zM} sē4 H19""l-$Ҡ% n.>n0kHOCxf8ĉqۼr,A@asx5?&mZmfmb$ Nyqrg;hdn7 yʱ8pS z9%[H;pʬg .[o89^P;pFItizq[[hvWFj$v]$v $qN~`s>o{ƚUyka嬻[{.J jiE븠vZ{cZ"o8#5jB)#Iã| q~+5)84=8xTW(haәXVРxʸv9]ǂsk񆔗]PYq: >c,Y"EP5ٸX ;e;#t#؋Ymn***q{l:='PثvyQZRwp3Qi$ES$[=}8$S׿ C:C.0 MfN˺Lq(2Ꚓ kgYټfp<*˝ňdh29GέӚYT3F)g+KI.GH]nbĺWy-y ǾWd$\}+LšӳJƥrNA cy#Ɔom-[m*eI<>I<1Bp`ޭ R]&(-㼼@qG$ xV@dWT$ӎ!n1|S*[[ĴIx)oʦ5\,qA*FEfi^nmH[M*MA.-^K6`D!VPfsKKZҐ`\E <KeLm$jZwDT1,8(p1p?G;G!̑IqF:QgoҾ; ՆG##БL y|99@3DZO rsO@"&ctCͱ8<sΗl ^p`Ɲٞ5G6 s\I$[%՝OiW8,A-Y6krn:<:KXc.["-OS^"ԡmo-ҕ؃+FAxhVqko+enmoB28N\JQCdŢ-wɐDF~)I[K4{I $Wn88nN:~vQ񑌯Q~GmV;f[sǍIۼ'r8WqqwkjP[\KćYG~kǮKk{%6W?ZڟmbMb6IėQKPsf.19 G3vڞaM=VjjH8%T,აW88cV^]k;94K닙daRS冓pf±s8tqY#&F\n6Ap@3a*3[#ѥA5C[ ߽@6$`ֺ%!- dUSwoqxD$,FKp6I߱[pjXdǘsIی lVľD&p?{ג|8G?> +y.bl,XzZ5^xXZF0`$:HG>I'Ppl=ֳh B6^o 1\$%s tFܫ>>oS9z<ͫ AX#vdBřQU%H=ʵ۶%VϰF0A`r}I>[ߩu' ǡ#T? ǯ\7Y2[\S1 Av\޶]%sǫu7zX'0Tiv+u^<2 K1Y/m~?ݟ[w4>׍E e# ֢G(S?He]#H3'O>]\U? 3'S?@9dbrO'OG(]֩jg= `OʯZ—2u?+A$cq&_^_DQODWBbs&鯹r|u/ _HtTP'Ay`5 DZ5n?!i? RWc] eWUIfQNB=s'S?I°7$F;:7j?u?~} Cr)ozOG([jvmzXv'O^kO}A—OK41ڣS?I€$,OZZ "8R~}O€2eK_z?7͓:dO?JrZ&Z5f7'u/;B( RH?lODQODS}.e{> D, $qߠxōWNmo. ԅ<~0:w52f?_4 e'ŵSyNO7ڄKB?1tBBqoҤ0`"-r !eD3a8 AaAǝiZƎ\h2ݭ^BEC'3;p%@Xtb]B0YXو2eszуz[s/fMe˔kuXS@_S[^Ӵ6+XLb٤դXW(,Vu}]ֵ0h0f1XDZNomkV7(c$ #9s֤3ȕ8ӨWMSUh5yIRo2\+o9Sc8#5 ?e鮦6Y]Im7:o=q%x5]GiVo,j-"ȳ)'6 I4..bYѯ&3M 9+Tr i&07͸$d=;pDq\3"rjum؂Yc4dF̈́dcvf"{_:XK4z)Bocڤ1a"M&g ZM uYMꡟjUzӷ0[l|3a Hv*Owᵎ1e,`|.X!%C5F CRO7†"/dX^fh[n@ XCS2jEy-ߺ_,P FӀF ,iRCmCgPdj(P(DmE?7qfTr3֬嶷յ;{xo&r*r 2r:kkcj5+Hc\ 3.~n0^M:l+ዏ0O|ՙm-}֧Vsgu$/,{HT8ɨO^zP4;u$(Z)r"7\S<}GBLרLPͧ/"٦Y%Vrp8ӧKk_0żXҽ[Ex8l-Ί.߽;;1QS/ڍ N>Y#)۵dB] W tz>p$/ ܣ2;N##8q;]RkKqk(RuFmT_0bi'5fjԞ×W7Q@Jʒ4QP$Ԧj $[Ry*ۆzVvg'kI. sn9A' r6º{yqohw$-rZ\9RҦrPVl,n>"ۮ)t h[{# (9ڷ/n$SB[Y%o|eI0_MyowϾmeuQb*=`A6hM͛DvG)@"Ϝ@⳺rQ:T\`֌uhi>$3C0}Ԟ{e[IO$׃ֽwVkc7*VFTaWq,wc=י` _.ϔjܨzI뚕SңqRHA @Zs1,'N9n*$' 1pszO/ Qx1yg\en x+fWiXã`W<ǷOZq 4}#^iKLq[ h i~~Qxk㥻>l-QHaEPE%RQ@ E%Q@hieih 3Us'3K9GcmԿFK}- 5hd^IypR6KiI+9'/bkI,y&8IC˜n>l]Iqh5c W'{[]+S4K7Ql2y1nxgŐx:lyI:,6fbbv(>@?:z=S4-C,wt%EAƐͽ[Aiv?oS:Nj|Coi}u4kr Fx=))[ūKntgݽTگ2Owv5Qg&>#v4]) 6T&wueecaOwJZ9HFl+1B&UA5}%Oz|9L>gB:C}M)?ۘqcu[csvn?ts&9~l7khb[//JY m0||%TӬ( α\KOʹbp:S4V6sa:^\TeI=nM}5ƫf/ΥP:`;J1Ԟ+[C '%?#ZI(RCrQI?[ ' Ua4tp1B( rrG8䱻3s1VJJjHdHmn'TA-iFW䑓ҡjۯ:+˭7Kc2f7˅<}ݎI^L][a7׸KT[fYn^` $ pr}y}wɄn/$sHe`ARwuUGnm\Q`-U%gٔڨ2\s#&u9cӔ37@yI01Jҵ%f:}#!䵖(n$_%ٸ?2W]|s5q˲fIo[\KrA K`YV>áiwV ;Kn"IFEH]iq\ơy -B\/RAA,jcsrzK8Kli/0f\lgޘFtylLv0uES'*~\{dfFIVPiVUzjiˆ{zT1HϷ5W3h(J;5&\tT2Nw/ \3Mֲ|`19=:zՎc;iAg]\7$AIUVa6*ӳa%ߛob֦'E ڼ/W~6N[GuӝQ 2I'̬|26(9MXk9SG˕ݕ.N+ti3룈rZRLZt:ٴE7l6e߰ \Un˽:,^Y97Ӌ/*ظi!H@cU$g52Sjkھ9~횜w tXEF[&HBv\TQt!e`_hyX ;IZ[\oèoI`yebڻ9kdFsxRa$BgUIz:gq:HmEI @P ?0?*SJ#0pC# +ܝ Tm:M-V܂FAAFpr*n nPay^)c:Ԣbxr4𬗚s[\Hp$ǖ`rķ$S_Qhgf!$"iٲ"[sm6gieDyf7y ŷF;s^+>'k]I?9eE2a]~l㍸-tӇ4 eYU_F2~*/X A{?6\,o- nc'ޅ'}O΋Q\,v/,xAWEr|C~| C~s_Y>@ƏY>@ƀ:+ohoh U7 47 43'F_Z?bx+,мoG4XUytʑ47ŏ/mA}?9u_x~Qnּ÷^X z/'{e-nV'E7OF,7a=B╼cX|,#8ؑSoېh'Pwi< &p"!pGYJ4{K)' ~{GvĒzVd1s2pRC8h!UR.3^E {V@K !L>H82~BH'd/t3XY$*I #{c% ,lY<ؠ)F CDcsrêAS\pnRRP(a|mnQZzKe$ww( 38''iftk׉[+o3RKETySܓFCE<2UIE\]O J.[)F](D19#g'XeY 0 fP dH桲⶿֣iuViY2Ss*@2Xy$K+fi/l- +VfW%"w@r3j'} #m?=l/WNپǨ\; 23]q AWאjtSٮuI2$VDXr:F޽iw=֟ءX#, .Y̊\/@sKd5H氹5a%ìLbXT#l+]WbMI΂[ieSYZ6A``1STV=&=>iʲRaGpJ!Ф%#ѵe5Y.-gsl\K,?hi|$lYA5(tE3ɇ 1 thSmehu??@brĐJNk4Hewz0㏥tTY^QF? i6NW8$+ԫO'U.zZiKKc|n#U+ma$Z-Z뵰x[acFaTu<8myvIbkxu%GPX7T5Wko2ݥ>[ES5٦} do kKm.#uu[ @ĹXQIb;x]D[L JdO3ZShep60ڮ1O,E(T`:da)=(a<3ƞ%ޏk5ŅG, " 1{5I{"m5dx`{Xm,, `XZD) cӊ駘J >'!YE&rc17r`g ㏦ip >`de~Vw0N@sHUHuS}:Ƌsg}Td}kXn-}l0MʢGLz? }inݻ (xO [W? .&!pNv;r; W0q\R|X*Sqliڍ ;Up91WK'+1ŽVu0ha *T^ŷjҡK 3gٴc+u &jgh<Y涁%؊<9`ݰjLIY RpǽwХwg:Y3T(=;6::yֶ&ce8|i_c'&ddfR|B>VwhgEb֝>q{}HŕV?^:#ίXx~Yo!X#v[y&f N8;ǥF,1yH_rl7`#8I⓪a|uy%\΂ୖ٥U'iK8:wLv\[\=rۣ9~^09=#Wq5y!g钣 2Iub妹< 5@K?#ӐEy=ެ:4"bu!P:Kx.ʂ-Qfrgx5ܲ$ϖC?ߚkBHXVw1!a1[FmsG֦f$OCd<27V3*W:#U]-$Othy, =s2iAz.v6.h&Wѵ)h=~.tE,N{~&cq?vf,,ñ'>u e*<9^s"ʏ’Z|9ڻqf I3<~|:K;,ޑ @\ >w8ᡌc8i P3?vd(pyss1֔@C1VGK+RÎ/FYayY 0R =E T>Oӭnv ʩ"BdVnI]vS'{pg0ȱ c`p\m E"ȻG>vRש,M(r;d*6ᑜSXUuݚ>6`}Iwo{ŝ#C$'srEv0࿹'ndL,6K',鐧ɯ4X[{mM%(y \Hݜn`FXdz>Y%*r!I^s 1઒84[XSRoX5}EQDͻYP AsF: $WbK[7 tpRFb 3pLt]GO}KZ{inT82D6pw 淞Jk+Jg|C4a*FOVLꍝP$ɲa*<.+Ł b):.w"XZ-(e4TpĂqt&qor.u-rFݹ#r4xrӆ<@%:!V'V;wSj,BՄ/yn;ľ"Ha&!Wb['k)V+[TtV0J|[qXo%—{`biBeȏys$^647Vw-œ)(PTɴ$ONjTF-;G4\qml+XV"lyjH*\Nf&7%&u:OU1gQ@Lw—a5k\w_bRKl7 [Sgf$sWۋisq"8U{l۹p'ל)=qX.V{{X[?tN9j!+4ỦX$}vy 7Mh3p*@zɯO5xtK6ҥ[vHt̉bqpN=2+;ifm̹k_SOD$wq,BA󁹷'q].Ƌbڄ%Fa Pv}1ldxVKԆ)%Kar!YP c!AH,vx xŦ_g$X$b8ocXsivOS'vZrj1QJ|Ś ǐprm]-FFug'`8y$a5%$19oj8NKy,gTy8t sLseK/$%ZFA!O6.g&JG?(,#wwC6,줊oɹ93;- ~nzdqӷ[Xh 2vl`q PNCoB񮧡^c1Bnc ^cE{Q3w3I nJjB8]kb 7 c e;# <{VV3y[U HBuc= 4ծm1M\,ʠAy%GaFBO^Mt*st<ͪz7n6kKՠ(~a3T.6yC8{~)qq^h;#"rII!71#a5lǛ;lcTa{nid,G͞䟥W!>Я4 Q˝c1?!n9D#?xu>swT:OΣ?J\|V=ajAjbvl㡫^V }G(L (BcLJ 0y'sN"$f%yz& %IlBWaϧN";YrxzБa({c*P;`󊌀Pr2;@KrYJ+7׷ 'w}k-SMIl( ldIHªzlV[oa{G)9R@X[5NG# 2͍ǜ;s1ON2ͭ4Y6:-Fq\CX.CCܭ]1(ݵ9Rß2+->PA$H-x <+Ҡހ5 9QK[X Cm%w) qp=H=)5/'XΪfYsepҜfLT(VW~]$nf2`˜# S AS,q,]Aΰhx LF9sSלxED%+26rTޠ4ۃ'^=ҷ!|ULCN{s{Lje~$#$syCKXVܴ1OKn,c >nximGqm}eDpT\Ur7 :@:=Ōz*Y<4qbcͯHSv㴼H.![Y%yQ Al=98aMxnӮ,eXȉܢ)+ e<qU{b֛v\(rw.wdƩ$=YWi'w_ԏPi{s\E2Gi<.1ʨ8\,m]f^LQɧ&Y.anyJ@Uwl\:<G3(Xr-9`FvB#tEu1D}-3>2BI ^? W6yu䴙`*cE.!!ݽ^6џ&)BY’3Np޹zKLmI#JEd0R'RF"2]ʱ6|@#{)YR<dBy8+랽`1O#9ךѤ,Ey۶UPX N z %9{Vƕ%$1hdH Q[?҇챾mi IofKT1T9\eIl&vy︉gh2dcךm5_ڷ2JN-D%vہ DX,9ktRuw)$6y|/Zj 3#$M CeWV cAJǻiˢUeA5igL%WTp@|x19$ֶeԛN{9b6*IUe 'Bmmp5fvRsI} 4ۅ#z"*5~P7R7bf)hXhԜycs֩hѮF=ե^O^d\` cjž.\It{[-qy,6 Np9`Vg]7'g^ mJCnnb\?;6QA~n mO$suEr(BF̆ t` ]4[;%VCfQ!e!ry:{[ +M㼒K4I 46MeNCs*96$޿:6Uxn2t1<ɤ-|P>k!.X4[h4nUR{aYREĭ"2s=gBRkK-㺹nG I S#A}5_OXi0g:2xAԴFI~BTt%| NpzdoA-r OC/mwwqч0Y՗ڿx5Mm&/.ΩܲUr6Fyx$DKbihS>UwaJaٝ_MȒ)̥!܊LqI>b :ؚ͛JZTFyrz+xծnDM rE0I|`;rlYﶷ:.$aUHsY@qom!hw l|XƜ)9'|S#MhIfFV8#hnܖ)C?.Q3< $c5<&{Oeh[ ۤ{Tb?Nsڄ{?c.mn5vviJLH]q~C H hR{Ta|TB'5q^bc7F(v '*S9޲2-Rc7syI%jvd(y>H$$HrזO5 mdbFQذzcEbꚅΕ :;c^q"a&562䣴[\#.;IʃL٭%g%7:EBk١Pn" + A -\ٰ ]L#{ !(ye<) >q]ݬ3Ge#ȇk1y*D-&[eA0XUɛi8)[D56yww±ew՘`Tw)nWcIjk+Ewy̆h3!`2KN]v7+}+fE]0q9QBsԁ뚯:$Kh$3/'_8RreUSLݼ,71$Ns\p*ζz$p[׹=Ӂ{`u*6ǁcx  SC Aqۧ=iU~aϷҟ'翹ć /\{ӆF>uTd(VcFq'ʊ Nq3c>s9JM>aT|Ý'9hP'r, 8h 9~>ne^HI})Xi .{}ӽH%zI99QON|d, <{;#9'),FḰ$j z}e! 45"@ʹ(J7@CU;^2G==QD'iGqI;eתJЙ!pqRrvds==LA @ ?aF(YBe|ex Da,Á?<{UXdiJќ<1jlZ# 93sG_jj؋}6r}wH_\GG` ޿X̟+NpK MbNؕA9=9AcH I% s 䎫$^5bBE*;}0 uKSbf!92}mҜ݀[*>*-g/#SC1+}3J2sqx_˯4*n)J\$LYFFH>cyP^i؛ʃ'*$F 20xŪyA屸=}c z6ME)ہ 3cTئ4[. C+sӦN?:lT_!xϰiB>TO9! 7rK8ӎj?#`WrF:g=ȉIJ7L:tIl7N:ztG*3*uC 2O {$jMB 䑌g}M:[FH%۩ә#993zu4Qu+I_cYY!fYVŜ>NxڳtjZ3^:^zJhx=0k V;yY>~,YIk嶩=N[ o UxnYF;Q2jDGd]ϦiEgHG.s C7~`s浂rtt:}KRl&ZCIuH rW?^zKxdLI;1cuZ)) O9xVd_t_8UG7Z$&/5C$dz[ң❬"Ckp&XQ+dG2HhFJC\V'sq&YXdqPN{Ub[ٮ4_+00"w98 ͍̪8R€ONҍ浉#D%rHCUA tϷ&I앣UhHm;vxF3Tu5k#8Vaq9~UZ?ڦf 1P1a8Iֵ=F0 Ҕ;IV@!U9:ck"4I r3'Ҵ-X G7/jϺ;{qN(U?fotoxx-15.11.1/data/images/script-files.jpg0000664000175000017500000002242012616075370017144 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:zonal-flatten|resize| 4http://ns.adobe.com/xap/1.0/ 165 217 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?>b rHQϩZ@\@W3תk>$wqýrb1>ڳvH.wEҜƧAoE\u 4 u?JaG ;c+ԁWBdg<=NFi dǠdi;GEd_VwGEcG̏ <Ȭ662?(#"<.4|ȬN)W6pTH0#W’IrX́XkQ[7G}UZ4taU0oDM+2IGA[Ao}Q oꮝ%N<؊ZNswf2&p<  2zZUVfwG90+Hd!E#@h#P}kS:ѿ?.Z5;{>e`X^ཎv{ 4s3vf7G+ ,~[xaT8g58Bi-j0jwvc76P@9yoٶ}Qfƥcм'cenxWwS}Cs6G(;á[=m[ m6ѿvb93Kӯ vzI fI-2HRA7b$f7GmT7[U{.Id`D1>vw]J?UB~uf7GmTY̿66?mͷo03<<ͷoF 3ͣͭ?Fͷo03<ڊy~lf7HUu}QfEʫooڇ[Uc'd;ժ6ۻtMk@RbZw^>¼Zg-fPIfSm:"dLery~"~ǦDfq$Y_ W#CE;FװsJ-銤QԘ*Ac4ƆEhޜ󎢸]BkcI4"8GdFnMurBݛؔ?)(qI=3mw.q{8w3֥RC)# ǂ#>W,ukѤɨF9.XN;PHI|ٽ ԟPR/A*0c4PBz:m)SЩ{.L0%,| H)4*K[hwHfg'E U1ӵ*z֖iQH<,QEANG O[IM%?.}*06GIec߀]FpD8椢FYBFHʏy}9@u ('+dS-bHbQT.H8bj*UPqG^ҝK@.ƅCO#?RU;i<ڟv QTL-3u>34vrFw1"p黐{8ҥ0A|fbrIgI8Q]T#K|ݘsy1x㼜]%p' ^QjKٮmn"P+*z01湛 []5,o"c$7+SúZ,Ms @p=vy4 ru$r2EX_bM;4͍fA{f.|| :!-om9 eD: 3JDwgSI$b@jh<30iq "dU;GϽHӭT87z5☬"D/  xⱂ Wc Pk0WilwRM>-HI,›eForp#NfGK@4̤=3w*i'+K&Žy*7g=8luSʐϧ BtFf\‘Iq}UbA`Vzxyb1b/V9G_ vD d< Id{tzhJW'!x=M>]kqis@m..7u9vQ5ZOima}7ʓ̊!bml$Fvmv $Yz#it˖kFDcM.ʡsQe iyhQ6El?5"+ʰ>/[ve"Z$ۂ#?t_EQ) B2ykW OD{C:E$g3.1uLG+,zf=DXm?>;/U_ ߙ#2aNmd>ĊC&-HE-PJ))ET0A4C@GU$dg(khcerҜ8@QQ\8cR碁vm߻1V,>#[+om) ^zִ,|eϡ$4PI(h{ s}tJ %\2Tjxt˫"?^F˘DW\ 4L:ą]ULAN\D5=mj>޿څBzc"ZǨ:H{ YV@#nzV7=SK'VLFT~f8Lo@4=UM+>iX$[tG$9iE;{ FjwVi\'#F# q5qeU.Z'kmہӟZd$RJ`[]eg.4Z}4GYH0[=yWgK`*I ۶n[Ap@"B8ϷqՏV'KH2ùu6$ʖ':=鉝, gw4;1>}?d<,(2G'qYUhz4i RUIћ<1z3r([H"ȮF;z ޅQo#\~%TIyeV }Q@Q@Q@Q@Q@Q@f((4RH#3$dcTLs#uk@>N/ͨu?~mZPOvj :W4P KYkUFP` (fotoxx-15.11.1/data/images/KB-shortcuts.jpg0000664000175000017500000004134612616075370017100 0ustar micomicoJFIFHH@ExifMM*bj(1ri%HHgnome-screenshot0230ґ'01002015:05:28 16:26:37Fotoxx:tonemap| Fotoxx:tonemap|resize|NE9http://ns.adobe.com/xap/1.0/ 305 420 0 C   %# , #&')*)-0-(0%()(C   ((((((((((((((((((((((((((((((((((((((((((((((((((( p" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?n"UUHTl!c: .?SV.="F+u]kUk־c+/>XUc?/=?>:I ɽqN*U$g>q\\X!DI9xVy"3!FhR9=GOHX ~ra 1SF Ζ"rX}q ;R-Foۻed&9>֗1%3)Tu _W9}M; $cщ{kb p=hX.q!M bi!1q=W4Z-Jh!]D5 yDa֒WԼCzmc2lPos܄8 BN艦&j\S#ܹ]۫OW2N^&(I3/mlz\} u'_W_^3Ś~!fdd۴g#>]O{-, ׂJw\ҋǶn\B0|yy773hۂY_%~j׶~/Ե5C-,Yދp޽!FB~4օfMtn7.v/7`ڸ xχAh T{oe+",Iv[[s֕VwڥwrKATE(Icbu!^&ַu8!29LǕ| @] 5{?QiڅZR:G=cҼB5O(|z./ju!Tx]i<֓% hO6.Czk 32O?*P9D,mkO,-v 32O?*P9k?Ji/"V#*-GѴifmo!wu~qT~qTr1j. CM8*{ .ivm.GވtOt~qT~qTrJGoݯ4YBD]2TC7^GoGA~@?8L@?89B%mhj@?~GP|> btOPz1 ~qTfoIQ#>iS鶯mm&2&z⁣U}7W' `ʺ@?8L@?89B;])4ߴZ 9_Z>=ko32O?*32O?*P1~=hH@?8\@?89CH#ֵbe] ?7J D[cn9Z>=kk32O?*@?89CH#ֶ7$ 9_Z>=kk32O?*@?89CH#ֶ7$ 9_Z>=kk32O?*@?89CH#ֶ7$ 9_Z>=kk32O?*@?89CH7 r:g(7Mb&@,8IJO-?G(\t|0%Ɲ 4cʬ`p~85oivv3{~ҼCʝǓ} R!ΚųmKɲWEۍg< 6z//`HOfL J+Q >U "Z 5@4QEQEQEQEQE?ṟ:[ŽlRk,lՋkZ~ܑd+qK*6iG$ v)FJȣabpsq*~!md[R[;;"Z7X.T(_rvut=wDmTwE~0yڣԴ{Efc*4d)iqrK(bd9bm`,vfh+ Vu"0ʏ ncq=vrB) Q,HKeuayX&06PEpzkKhULBԡ ^SjV:&h6hV9 9$mŁÓۜW[ޗ[#iLN^xt.ݴK wzf<Q_Hʓc4Yb19pY|YE<Nm/fk)?p*nO'Int`YVp (΁:«x~5幹Ʋ ڠ'?3:nHa,FN#A YDhv[0#rWu 1B4@?jݴn3qU?`wj[" !6O5s^ )Usup*c)mY|ņ1']F: '~b(= ^_hi.*M(eWFށJ|6($ * 0:Q@Q@Q@Q@Q@Q@u?a_CސGpwֵN]Z?D与F-˝: wk׎+7Z^!Ҵ]J/E̛Mb[3nlXv 5_0eX ]qֱ/<Ӿq)g۔}9t-WqOgM/ ʑ*6dv O#RB6d`dmpO2xTOM(onlET LQ|RC'/Dg>b\yڰӭt(vѢP*': 4-.9 9 *֗m-؈"٥HX䐸\SivMFM92ܬRYݶ֜HǸ!dPK_+./Cwm5إt/Of!߁٪!5(KhtT%c{KO!gq4QMq4C ( ( ( ( ( ( ((_Cޟ9O SzC:7 A%҄1K! H-t?XP7g#Llw+цG~nz8QEQEQEQEQEQEW3[ڄֳXΰ@38 <髋/?A"cLX1~?OC[`qTڎq{RC'pV6+mu.Iܷs/!\"ӵI/CdfLmvH6qZ<+4W4o7Sýdw[~*7r?rڲbo. ?<;vg'kiZ{{4hv08dhž&nYƬŌOqoq%w`p2? תzVkY[p-NI,ĒIM11ul韻e( *.b,۶  zƦ(]sImD.bom6<}69ZymvG;m*prJѢCdS(((((( }?1L=00kO]kZƭį?ϚRBW=0@3]rq\ޝȣԥЭZmJ9prY|'u=D;J(Q@Q@Q@Q@Q@Q@y;S { [F,ؐHoU98u*_v7k5p ņ逤R{6%.. ҵBjM\Wǘ<{nϝ@A pvޅ\ZFdR:vu߷ilv:TZ,t-Ud #!up:th[j]ECy^Αfs1J=ux\W=B56Y"Ls#ohEq%ۈ.]P"PɦM)U<,7$۳$s-<-[ɪUTW\?8=*[4_+ވxʸ,U /BSFw'd]_K][i]p[<`Ԟ׆u6HEkr`MS+A0q./k+GQ̱;]3o`lt `iG>wKwc'ڲ6 V)VR)K&V2B(aEPEPEPEPEPkAb{?aHcs\[m$ɬ/HAoXP "s  \[jϋe吝ͻyOo>k~N[%g o5AW<3imIyq^ܛ1FQW*$W'u8Xo${48QB*a%qKdk5Fग&\yAsA?^'h{'ֹ4QE,R7`$"P _ZZ((((((( }?1L=01q\xfR ny) C藨& 5 VyK#LR~!sʐs-=K-;AshŢ,ɒrmR .]Bo5-n:u#AЧ/mgMʒUE13F~4&>OͤXḋ˒ };}Jal2\ce( M}yoag5 *^I:\v(ݦoA!`qxE- c5ŭŊEQڲ@##'qY}ٶY+pQNOuo IA#۽5H`!Oz((((_kvcV~bCf)a>W kq[ {"3ymuA觗L FRMQ*U?*k*Cѣ%ML$ܹv|SawqiNJ e1b>pH;F9>0O:m@ Y4Wc>a99/gE9kGv]n1t$. ؐ"޻C{o-:Q].B0F s>4KmHjf6y~㳁!GeA]XIRQEQEQEQEQEQEQEGZskAb{ֹOec͹>PLN&Ks[qI9x4-YNJΡe"ʱ_N֭c Xq nR֭Zx\ZKi5 RPaX3mGkY-Σ)Ȱ)x4 nF|Mwg{Y]iޣ! g?5kک6f3°;34_GM=ŋ1RcYpγf徿us^A$Iݗwu0E^}CYŠT g ɓNhfBbA(~lhr7<9mibfaeDAH~c٭g6ٶj.bRT€63Fk5\}M*7/!ڗ#Md)d8 њ%=?]mon+yMK) 8qWVqOjl3(Mk4Vdo$.nno#}4d$ )DKjXz 5$~*e5[Cko&@~3\LR[k͹';U7UO(h>7pmflZ&t-Pݷq5.:~#(//Ɛ.:h5$ц,l/OVs2x~{o4{ŪZif?u{,Eyr#ezzV4*C+18V>c=$4qJFFBj/]7q#܃}r? _&֍ƀ9?P)? mCkhh܃}r? _&ƍƀ9?P)? mCkh}h܃}r? _&ƍƀ9?P)? mCkhh܃}r? _&ƍƀ9?P)?-g/?t;EtDM۶㭶-in%(5V\.X Ien)'(js11F{5v;K}t}jɤ0Nk:vYYY3K8-p 﨤.,eڅhC/Fsz 5Im9VMN0dR{vUT_q \im`VDHIAPPi,&֭R}2]/RiWЍ"=wTPyƁMZ5K=G}*5_$sֺi7'!wɎKp:[=~4=X|0tMGMѶxu+GţsHdS? J޶хZi([ a 5Z=h`y烴 a<`C-^Ż#61ڽ#6mIXh:~~]\w:W WFihGZOksTW i!,SpU hUik8\j*0Ee? j睟&mj??Gڵg mj??Gڵg mj??Gڵg mj??Gڵg mj??Gڵg mj??Gڵg mj??Gڵg -_PaC":Gyj;5 Rr۱PP[ZmyMo*tqAO?t/^^iL͂aO/ng}QQ) vM~W Q[[n!X , 2I @ g߂<oSs@oOI3@oOI3@oOI3@oOI3@oOI3@oOI3@oOI3@oO=Q݂N_B eVfGS!ph_9?myo𨿴"7/G_h_9?myo𨿴!7/G_h_9?myo𨿴!7/G_h_9?myouReD<{j!A$"G߶ jeī5R(ЋxMK'9?m?hЋxMK'9?m_iVݙU8t/OhO9?myoINP#('/EK'9?m_hЇxMK'9?m_h1"΅Pr~(RA[GIPEM'9?m_hЋxMK'9?m_hЋxMK'œHu\fO>%IlUmQ@ E%RQ@ E%RQ@ E%RQ@ E%( ڠU=A-mkd;Hؕ<Ω9xV̑X#1ʲl*:PΛ=CeK+Yّ~DM=E]m$ sYDxCLȧ^y< Wvɦ4ƍ,$L`HqsSM]V8--HZqvBk?ikn;CЖۻh%Ʃep2ڡ˫}ӿ ifk=NDdQH~jU} ;cXHӄE >I@fotoxx-15.11.1/data/images/paint-edits.jpg0000644000175000017500000002432012616075370016760 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|tonemap| 4http://ns.adobe.com/xap/1.0/ 258 342 C    ' .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQROC&&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ﭡWtWwa-'kAo@ʹfIEV*۲=k䎇E JΚ)-fШF+k@kBy}1!N<ԬtY'9l#)ӟ+Ӯc0įEeݏY1+/T*s}h״v襞(@%GF$HdaFR;7-y!b7^ᰑk@ wptyqWi4yZ9HA\BdUB3`NqPs4nxf6;\3pgYϟ%`}隓ʇxG| ɿiK"1\yc$j߆Q2JI ~nnPG7RO*(1Fʃx| KWFhJzIFUw4fr%oRo\r&oRb_]r&oQUs5EoTs?ZV 7ʓ7n x]OB ө.R/ʍ'G4@#\.֕m'/ʬ-&' %q(@ 杂eOQ_]G` `Ol*6R6@!ܩ#>I`p~\tާ]&`xGHzR.܉|\ǿ@h u-/s arsjDgBA.Apq*vJ˘D'4 *c+4]S3@ :-AKRUOJj'UXfݍҮEuotۤ?*I4y<x̓zRv[~HvdjAk%p!O7ͪxPͯE%3bビIvI ʦ;ʎT26 ?1t#9"h4Tց$y4Ut>IvwbBV)+j!|܆ÐG<:=<8["/9#n5% (Š(RR/?Ί/?Ί=q>oʟTHQEQEQEQEQEQEQKF((PQEQEQEQEQEQERJQ@~]E~]RQǴ_/Y%;W8hm:`$ݏ~Df77Z9Xx1᜕hnWd;H,^hɱՖ˅*PyrHt( A4I6܂=1LFE=p<)&s3\'V-QmJ$ONW<"Z{YݳIRRzhDݵd#NoɻC[\3Ib|IWO>Ze ū;_I# ǵ0/,n|k0pBWf622Є0y jB}8BN$;sMmm_z"YATHdX#$ v-uD$y}kmlm?@ mn yu (dDQ&p#"6Cֵ%)06GgkQH (ԉqT db3MǛ{(xV>9jjZ amm=?5,lRkCrGCH U&|qCNTYXGyUvyVVͶTuVkKh2sցӚK8幑P*m#J.p@ElPB}?UVٮ^PTߦ=jO:-3ʀ&oz'*<I ShFܴCws eH#QGyjx-QEQEQEQEQEQEQEQEQEQEQEP3yFtR^ѿ%!5BܾӼjȰF8;[vx8dHAOЊs?Ψ`dv/i(OX#. qW?J?>7*2,wًGJ``֨䷻}a'hT!`6ߣQ9T}>Qư$me]~Aޣku(d)+rbNI?ݮx$?:L?U ;ـ.?jj((((((((((()E%(b?g?g(``-7^Oj~IoʟTHE7z?oz?oQ@ ޾~(޾~)Pw߶ 7߶ uE7z?oz?oQ@ ޾~(޾~)Pw߶ 7߶ uE7z?oz?oQ@ ޾~(޾~)Pw߶ 7߶ uE7z?oz?o~zv7~`P_Gmo_GmzvoGݛP_Gmz?Mߗ r:ʞcѿ^ѿ%{E*}2=q>(((((((((((((GZ̖ 3<=Lu)x%xՠęYM^&mHd`A,jed]UQԜ?}s3u)r=58H9~u;Q*f9L5j.? LS+?**v[-p 77󢠳h_OPj`OҲQ%*[(ҲZiYou+/P*[(ҲZiYou+/P*[(ҲZiYou+/P*[(ҲZiYou+/P*[(ҲZiYou+/P*[(ҲZiYou+/P*[(Ҳ[_Pd_eiYou+/PƗPziV_oJ{A?񩢉!M Ie= ?:@w?toE,u+eY񢤣fotoxx-15.11.1/data/images/HDF-paint.jpg0000644000175000017500000002436112616075370016256 0ustar micomicoJFIFtExifMM*JR(1Zgnome-screenshot6Photoshop 3.08BIMresize tonemap http://ns.adobe.com/xap/1.0/ 8 192 262 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 ?tMKCӤKwkXВJՑh-tmFyX;}zr*M E3?jw[e'=œ;sVt68-m*oM#8??³wQ3k+ۀZ9'0=:9Beѵ`(bd:G!qUBpK'<f:5oeey=\Z[NcbV#)E.hWçZ$n쬰( =wHNxEA*`zwVLW)o.&IFAiOaW_LиlC?e<=bWZ.?'6KkXq h8Q=ؤX,{IzU>[hB澦Xҏne:v?´ cKag$V$67EX ȥ}/ ai'ʃo·i2QHd4iy_??'J-c]MaRInb l&4^E4I:iӧM[}54-V)$1ې~ϻ?OjX~4Ҏ+4 .c4$6:nVQ^% 7YI:c 3$r]"fe9mϷ/`|΅:N@;*vA/$FuI[h\m#KZiQȐ1W[tv QW|Y"^EAd?Բ}<%q [3o#ʱcnj"??ytnO)}K *5i=/?2o*;OyTWW7:NmO09&5kY7G2`,pFCRaK z^dU&kj6ypvp!j <MR`{ RNQNN. ۀ0v_Bi/4dOޟ#U+?dUV߼ɿKtgUiu6tP䞟!m@o*&?/I*?2o* 7O+,ٌc=?*8#ut7U+?yTaYK U@~e0uic_K ^\1*zyDV&?MQ|Kq ܔS<77$viP gҧ³7Gɿ^9<%V*Xm$Dy=_R]mB'WMQg=/?2o*%S(+jhb+Ҙ OntmleJb]³7Gɿ[*]yq,v7h򥕋gױݮ71Yi{TaK bl'STǻQX+k7Ėq2%y0N}((EZw׍oi Joڤ+3M]o? =5-:e,ةRrq QM;Ro9 NHՙ"acX$jEk,?Ȫ K&[KO_1Tn<<6ks [:dFjɶ+"yL( $rGnM, hJrN4voakv7Vuob-ݔvΟcfW!vd3u~,HPWzbgEX(I;;"q ʌ[dXLJD1o Ft Uྉ\H"Ȓ9#?e:晧=7+ i6kIcDQcEC(=zγ/m-heI oM 77@Q@.#0Āq\F{pHV Vn%PNqœZ$0Q4O}AOSfYR 72GS!f8;GN=k[+4`038YW^. զ@[GPjK0YO*j6aJF+PD"1}ݘW\qL,>b3 QEQEP)^)G?xaOEXx[⭍Ow{9^7 c|-`qWFy>s{&OaN>NS M{' }? gj8AҦqX3@^#j^r5 ^$0NF9ܶځRI\܉A(yvVmEohYa R: ,_/̾E9˫:7Q ?N$ ;||C z+em"zt&ܫ]<(j_[<[%h`݌tϧR23IJ((>I$3Sl1l0HjaVщfW?!22~-X+?d6^%C"FhedNޠ֕(m@<0oAeRQERJQ@?xaOE U=cM[o?3\o]N30^Ԓpp(J*pn$74rlVhϸA<́g=Un.^ݯyOӭ Xż L2F3٤2 I.ݶCNOjaj`vDtdeϪ 1袊(()r~P7F?6v?P6Oy$Q@Q@Q@(xWQ^)@4s踫rql+m|#c8e =7o.*_ImV)ʦ19*XaH`1A?&DŽ mNj6zUt cfng 1tWR!w 7\⮇yr7o.*3yry|uy|7/iPw/h眿Q@ 眿yrE7yry|uy|7/iPw/h眿^[D%@ yry|B{')6< 5 ?,r}TM9F< 8`wM40.\(>Sw/h眿?,ORhYq|4o?_?'(г?@o?_Ѽ) 7D|`s> SAz(׌? s踫5i|-`qWFh((((((Q֫Z\Ek]\O+H@'jƥQ~vM;jkkz^,o%UKs><34vv_,rzq :;ՔXy3;vzz}IGR0VA;K[=2i--滤܋kB8>Hmh&iAn Sqت0K/mHD4k `) A֌WZ[w6Η1],mboY<إgn9hԢp2Ry5j` .3+a'fj}$Acی9']jgR??;^*G?xaOEOE]sM[o?:J)hJ)hJ)hSV#x'`dwV7W( 576 878 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{\ymN #&!~i:.uAkF޾٨(R$.FDhƛn/^.ۍ͹vzt5,v ?@oƗgs${0ܮ:r+6ik52_4# [vX~{/ (L<ҲjBo7}zO_!8纑W>{]DFI{DYwcd~Q`CN| (ӿlԿuk>%}^"*?=,W{I<%2_-ʮ挅^pp޽*w2]")wlvzW7`.㹍'i '#+=Xb̤z3hZ~ݛ;.:-3p%Ϣ:ݱv1]Tt Ǔ4p c¯QO t [û%0)H=Oo'57s:Z3O/f${T$a}~ԺZJˤB3I #'eB@bl/[YMkʂܶ>Z6r$(0x 4Jrt9p&b%dwI:8_ Ǐu3k{֖iټtYˬ&\8jy|-hn4ācY|1]d'WE]mFA:{S6Mk:+dhD,w&Ve+7ʸ=8ֱ_S[B1Y*2ݝ)䃜kheưGpfU =;_ WQ\[ں4 ^7q1ڼwt5 N13@jy(8Sk(`1;嵷 V{;H 4paFcГR XZ=V1\# sLa__]G!3M iFHg|ci#W~uLbK@lvkBRh!Q6qҲ_MO:4aLE c%[)\_Q61kDjj~@FG^Vٚ@rl$qd(D?@Q2*gVUa.iuH#Q<61O+)#+c%gWW?*K>SQTTTlw_=crzvK|iW!]`VyD?@@S=09LZe;ɻh9L$m>Psi_ϔU09>KRlH[EnI:Usi|(o$.#WI[~\KrN/T>q,rG GĖ { ~/O}GS&oBͧK$n02=H\!\].Ȥ07uV}-~ܴwJIHN[yOc'ڵ`Ŭ+ $QEO,Th|⩁'Q5-EB׈%``r{RKQ38jl H{zB'R_ x-=~+eGw׊i9xLwn].+D?G%9'?+=K ;ih|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@T_h|D?@UG?-jFd{iz ٪5 \, >iE[ȪJxܛ48b̌,{5>rYtxJ&kgDE Ǿ1Q.T!CVJ 6,q;QzCҭ%h02$cStQCtmfuCŵ̘MxkʑA D' gp=NUPTc2s⫫[ck9 cӦZ.xXn@8{kidhv5i'18^+o+4jr3Rx=(EΩcwq*n\~>R *KCϵb)W;hAی8qH pSyi J nf$f{TxUJa%)<0ds?ಖm'QEʹFi rl dp22H"ZM7tE̪f_ݷMkQ 2ӭ$Hf.$AT[ ''< kv^"D9`x rɹmrݐw=yIk[i8A$/W(x⎡ֳu?mrΪA|s:tlm|VIn`7`eMrLZ8"DHݷm’yjVqm~G4sj))m},5˨.N`$Ŷ€NN mNdhkeı$20Չ58}$eru=k h0! %wdL#zWe%ϔ/ uo(e]o4yO#T`#7] &eXɹ`94 ,ˆq^F2l͜d1ZrytdK(1ʹpHLKgYo;5Ts(8SF4 u2$k}"˸W`r'zJK-%բx E;;if%_j8͚5w>l#sSCiKm$p?("id\- 1Ykf{)5ū]"lVX{L\x72_aYZZ!blD?)9׊Vmr;c-C7J"p9ySi$u9l!$>Y%+cqdUOv=by+~]4;S}qL~s",~t@O I+_.K[P򪂑LZ3dU'^WHᶹl1έ2G!m'`XZP\iW%1CK/2I*E#-By ksE嶉su<3+\&$lho<(Wu&s<>B>`H82}EF>t,8QZq~D."x{&7(`wc#8Wp^8+8M`Gv7[(rI1G=AR`tS,CNEiL;ÇoNMsڤWp`djmd:z~ئim=.9ݷ~on GV׶/3`ޜvl.0i,4+$F1#g0ٙ-ezUr -)ns-/s{.nbb̋pd*E.$1|y⬿5g^) B89(@żV:yF5nYenw߽26W6[t2OyNN@e^h_y&MxfeːVT@#ңe-쵔PO(xEH2.zd$ 쨪]ӿpY<*.@)\RK fkCH$ >'L +vWik{8#HrXOڵ.-HAPug-n<FGGLVa1^]4Ҙ[/5`$AA.6X]8023|R[xl.@1UZWtU`NG̘=yg]zm K{{xmceH VbG ?<fCi3ѥcqH -M{:g&R퍐eK`} *o^1+eksꋫ\b1S砠:oI8E'$Mjτ`y'q2-PӁ\şkSHHR'2_$޼#{xt4j.o^wk彝 ڇN4'mo-u˴#oeG\^5? Q ƿc veZ7Vnĭ(B"`rG8.o%[yJFNrOL^ ƿc ?p0,@h[Mdi3>ȄH08 ?Bi\^OwvBlUl"*p0,G.F(HYEZfȵPL#@Xo--ژ#Ti<7P+p0,G.F(l|?qiIKqPn RH@=VhMRCvK9<|]P@aXp'vn:z nnNsCs{zW7FiGP25vM  8>\n.8L`mYy^=yƣi+طZ6*"*Lȱ;`` {Ѣ);Y/K&h8!UPWplqp~7S 1pGRwcӎI_Xz~C(9dJ Fxޭ)]RIz=+NDY'K,l`"rğҼVgg,ʐe\_x?Ļ┳!cѱǫEiz"+M@+Q4!+KdtV#={l> 䖸)k`V,MĝTX@$\݁le֗;ӓ2:x([mo[7BVd9ȐT ct5⺇ďѳ-QʴmjUw|K 6D;G*vϽ;44oj^-=Q#Da*$NF?)/&gC #ҹ/~0h촻B Y9#W3 #_+hДhЭhϩ;\`¸,'iNќ.Km8:?EŴtpP,sӎ+ğBO𦟉~2? OȀ\<<4KbEHy-lׄ3? M?f?#Q&˿I{{M6Dwacb<9φ.70w^tV @#qz^H~(x0,H~)x0,KL.zy "u !g,b;qGZZwi2]6-$wy7-?F)?jx0,KHw=Z/== r2ۨ{lIm3dtm&4 &vx/-OF)?jx0,O&[pZ-^Xpr>lp3sX_ |ASӬӥĬ…B5G~'4getn#1+H)ѡ*J*@}b hA07qFOyU6no+# &hXsA\G&~%4_" _bw3x^IXsE#LTHw%yqRhOW̷Kr*7#epx<xt*K?C;iyoOj&7b<>c0H ۻ#~)qC=/ \ikq qNm2ޥI }: }McІ=NVԜgEy,?? M?<_O:=OY٭ЄD4\( ˒HR3f젹Y$Y[q1x$W0'SO[?—V#9xVhG&i'hynہ ۷':3xۡt ׆QK;M"T 0`Џ44:(f1!G?iE? ?Q:kO>RLv3#8ӝKC&%sϯ4bso}N*ZT' .[ GM-b5m ǭ$v|2b"@r`@A3yZHe0U.|yYrtRgs82svjo5rLI$F0=LW !e>5ۛV&Ksq$8,z㷽%+W9웋*5.\ۺfU?Jھ-u-GX=7tˋ̸:WFD輕 'ӹDQThKhK eGlS4/r0ٌ Qt݊4uW:ҝf]Oq0@I'?AQG' 'J\ݤK+r98a=Rݽ'ↄ#޷Jdo?O댿k+>CF25$ e*="5hu=Em"`K!(pl;tN¾2h [&m{Ջ9vrQ`F0$PUm-4k"$v; ]> dž嶸-K29d8V2W([8O.Tr8֣g|o|Q'*ۙ!~p3g?Uu=i|O/7*HKC``)1Kbͬan#k>[##\U(iWhQ9Uhx[#TFfӥLTu 0ۭ8=+2OqmqqK+)tt!N0}&MOKd?r+JP}GVtj"g*[;]f[+[yslla 9''j)fdգKd,@V~x$qO©A{,I2Xpd+Xvi:'\Vft0,=;n5k ͕p< `SW͢kz=gx7a[j#Oo t˳!8;mV&g&d lI&iY-[go]Ǧmn ɂo/H=+*Q,aH3e6y겲 #[=rGJg:5 `HAl{xec(^5fcd!xmz {d'WhÓ|7anz¸o-!{lG"28^kqVdvlw~)S)F c9kGYYQ9-+eO nE#eZ˧UfoHሔ 33ژ{x,'Va0?t*Yp^+FHŦFڢh}iqUwjCCQQڑ\qc^ypUL{Z+6/Tmajt*< =qC7J;PΪ4ts~\BO:I91HcAR"E$#~)f_HUAg?V'81.{DyICGj+ $xr@uoy3T$inEf֢X< o^4VrԢֱ1UgTwa0xx}^v2!P>ըc@Ja!MBiֶ3^fmv'81T*ش1QPbGRNi^:ȃ-k 湍GS7b)X'ְc!J7:(ӕFh[Dn4*?Iqh/Ax#eOJ>%u]LCvNaފ(6?iE?Է3Hz/Aࢍ;A( ?\=#V$'nAˀ1ƻO]B>ђN04=8oc#ڻ_u^"K˩//Z%b g~jGG\\0Tmmu]/P(;PZL #6qUE;S^UUp`Q_眶&8MDD=aSUhYM (>f-b#5 V39yc՞%'0p̡f82ΙLV{QG6ɕ$cyʝLqrhɠV8+MK C9i׏p ;jZ|=2MjF,R;gpH22H$sf1$ 72)PO|Nk-}$+~k蘭%5`%ei;Ea#;seFHZ$mbP8dԝL} k57 E}ں]Tv̸0`G8j(Y[{HD$Z xt{9{0|'8#jOm ݧ"#=ѓlƺd<+,Bey?ݮ?\V[Tj,r>kW@mo9eDa`?g9sȤts/рSRi y pfptvmp-3;9l#WV==< BAcrsڢz+5/Y@!nn%$gi.oGN@ ¹MEeo4(F@21= f]Ak?Hv .aoT3! ּo4_/ZJ@hY]YoxtMPr 8s}ķAe5EݤcF+WP, xmV;@Z۳%Ai³`|?ݥ̯bnb֛lC7w/ H;XMΦd1; (93B/ 3p$N:O?R BHF)p:cUJO[[gX Jd3f:lmL1H8 sS^4f+gb$f)?*tPzӶWBܼ,$ ^QK-:FIȼvu{cok"F^u:4MJ<ں><I>zpi tag{5uڵZ«hȈ_2'b޹z=xkoH;Oq_XS0Y[HCvpM6m5 $FȠ^*o -_4cז {HD'v`~`xX|',ƞmx098N<蛷:_[LcNFd1^X,u#l4'hE4JUJ+t;hy^L:"'BjNЋmSL987(#R:=8Wtۋd阡Ldg9x;C2̊Hg#==(A{z ({V9xIU!͞ hℹ̱ȋ:8 z@<|Ӹ'q> 4TU.~gXUci sJgUCmw; 9wy;G_i-L1n%K}GZ7Iq$_(WwAجks5M#WT[g옐rĥӿ&K19ɨi{nqFO:Ɠ'^EYS{Ea'AX|LL9Wϯj{Ԥ%b&xcd{RƶZ47pFŌS`P:W7p`vdo\\nLwHĈi܌X%bDl. Z*~PrWNQ'lN8:\I$R۬j [?] w.eKn>HB݌z{V[iZmpdӜ+ҵ YROץK,ַkwm*I,tu/[F,^ ~CjJs-ZO j$FɧfݑH7C,<~s7}{ kQ[VVkmÅWy3P@`ȋ&i{nb:wq]6FpJW| ".Mts3q{?6G%YyRWZQP`f1!G?iE? ?Q:rL#V2}:eOoAeZ@X]o#|IfUkc'p@_$VMvufb{LEw(FH9pj-E"il䴍lTIMQ{4C6Vb*=kH\5żeW-r#=F9Vk簂[1'y1Pr03U]4Mom{-{|` +ed^f;E,n`#uF2`F2":}B7[F`gY˕#6TF@;FiR~7=Vexe |lpwOAHH6tC -Ԗ/Cgv_uOs:޳I3‚1meK ;i Ia&͌CErc:TZL^";Veϯҡk&d@9Wԧ(Y#Y}Co|{v=&yU5.a4XzWX"btIYQUQϊSQݗN?u+otX O{V\ ̅/ܞ:kMD&v cRi(3| 巑wc&:Բ7@~_DY(X/pLSʭq<ƪxH&ӵ#2%X-;mBB  ROZӼKm^I6u7- `>_Hn.C!?8Kн}~}}i|qJ8%ү>cv2Ԁ} 4_6zjZM 4 *!/KZ-дRL'9v:2+9$+ 3,*{~EWD]۠ҸʑWjcElFܞx? 4@0ϸ|^#T'IzJ* rzvx\^]Ag%K"E$8k7M>'ec/|E{䥾zʙ,Z '=>&jvI AH>kP:x݁q gҬ֪1QVDrN̑cEnFN۞=ꍼz 3gj,8,y=@OitZvV<^5u- $z.l/L'yfd"!k|G}jdG*6o*h׊ .O<Tӵx|}Ez}hO'NǐjX?|I1`k4{y^Aqqpdd0@3WsZy֓c>n'sh1aB#LgU9 zO_E֏4c8/6W7We#= Vx :oLKr1zo4Z2oo=Wvb*s ESյGcd=5~i>מY[l4 麎ëy^ZJ W)c^_Ȯ+|G}jǀ]3CESTz*.*)Hqݲ8Oi(F lp7GȊA}Wx"WñS`@bNq]}iHdԂ)96:)VU Mqb=+s3of_ K\4@jNt (ӿeO^L#V) (P(W>|{IfI$}>p5J&@0Drq wD.f!T0UI8ݐ eފY#D@Vծ9C6Td`z9d'5r]?O;{.HR{#4`eA)j6w3ڣnkQM:P]V28OE0;IH|Xэē|^}>S,+;7ѠӠoLYMr͛}Ϝ~Ѽ-4:|M˜(*Z%rzmdž >`vK\1+!|7}FE 1pq`Vt$ G:KYK$/pPq޳EօA*& w?z֤^º;4*`si&N$KXGdiUARM t=8J=~jG5HW4{ :=CELZüFt swt`rW `rTóD?{[D07eW< |3IoÄU!~qv_?#%:?C: H$sRo -nM9z~Oj *Amzu:7̝|\p9IWb?J_jԟeZFPLº}FhrG6KK3\E)Q ؎1+KV+qOc#vxҶ>?QA=\'k4'YŌlq+/si),獞TFUbAH'~?QA=CC &Y>dQF:)dѨ F22A=Y<<j>?Qf ()dѨ Eh3̧̣Fz5a22A=ߕa22mQ/4#(*Oߕbm*,B?22mQ/4#(*Oߕbm*,B?22mQ/4#(*Oߕbm*,B?22mQ/4#(*Oߕbm*,B?22mQ/4#(*Oߕbm*,B?22mQ/4#)'_;_J,=(41<#ko>IVI\4@jG%H븤kzuj5ݼ085Rӿ4Aࢀ.Y?jWS'vՊC  ɇXݺiY.9aֲf^HF%k#_jAJVM\!Ijd´Tk_hhNc쑏4}>E-g L[*7\GQw7389\ܿ\RTA="^.+l݌_|6Kż$m8`A^X##P_M-rK `p0{w?!hX]j|fB0}E]Mh9yd9% # |l\:B;zR[=~ b2;3A$q$cަ eCm"#Hibp0督֞>hҳCSq'~ 78*og%i'%hI'Zk>a(VhBQP=xUm5;Xe1Z)qg34V=h. ƛx98<'~$퍼w;D3';#{#\Q4%*Ar :etɩѻ3-sD|;Ͽ \$2[`HeP>l@ǮOj f^TV67zbd/=*K+NEQD eAS9qOR>6fXɋʳbѮEqhZkcc,{ufʪʼIY\\F,ZDIĸŀ}Wx|yJ=q8l1 wry4kH%hm-߻$=K͚8^eİh*BX*k K"qNK3UD<7ooDQd!'#iVa04gHDQXN?3o&AeXW I+b-l`Ʋ.N23qIj0ŧ3lLQb/(ۛ$b!0F)bG@/cil]M\ZoFoҮ.qy:0UV02Օ̖`Ѳo" ﺀ0!|EA4UrҴ9?9w}xvUJľ&?š|Oa1V)LdvM>*?P1f1kJȲ8(n.đ(&N㊖%ttJ ( ( ( ( ( ( y¶+ mRA7\%\sL֘M%у1(]3$!W{n1'^}y?V5<bK%6v,"˻lYh /SsW Ksog#LHNI8cdϥTX y+@K P:=^oی |sp5K(U_ʕcwSūOFh7w J#b"Aʎ>b/d8KyVyᵙX1 n{ `t6ڄ"|HHo 1/^T:sY[7R2@Jg21-:/[`p2 H 5:A#5CmC19 {9yFӆPpAŽ5'셁yт24$AIupK 1@S8LX&==i'L|P h@th WK}t^6g!#K6i5ɕY ]iSK$)ϭc]\B t?lȔ)0yB7c'ԓVCsAr2T؅\=rzs]{Y)vee "%愁"kq2\V^5~:h/.^L̋+Yj6ZQ(V!6_be:I?x[=GA\tDOj PM$:hCɦ#Fp 0>R ;A(Nt )rL#V*?FRP(Pup^/ɨOlKgc8#z83]srf%iD%ѹHAI:ȝe 8}'+o%Cqr-ě>+.(LpA?A@~E-n=p03\"wA,JFm>fݴ/"r  G^VϺ#pU Hd7fMγپ˭R;y=pAǡhC/X&zr?:SMoIs;pUPx85x~YX#H*>p֘f)+uxM/c7[,6y}Ϡ 4m\f VSv3ofܵԓ=B[y>n3 h%ߗ-X^4Ey>bS7@MBXMO(A; %S8{V|QxAf [V$a$gFz;"HHydTD?Ϣ\^.loirfp9:'(w"[Xn(0$p/fKׯۡ1;x9xCo<#I\*(O UAm n#V\8\\r34v/5[u1JWMgH<-|`~-J{QQvtSgqF,FsȪZQ\ υy F>ƎbiC _4&bd@48 L{QPmw?]{Yks5n5hȞhBm`vp.WV kyFF ?ր0Emqn.fhd+v/pnSg$ː0v9۸)+w"WB u0;S1N ¹Ո4c8YE{{!#@՜orµ`afW+L+V l)Z{ F Hw>VN=i]A8H|ƚv'8=ɕj:} o5$xH؊}nu?_Z^Gjω d1ǔN {Տz|_C(e)RTid`9/wƄh57gэ△NaI:mJǖ=NEV5&]/,7#29?oK{]9 m]V/b>Rq"ԬT=SHk8 Y=Z}w$潴2GUT*[ V׈|cxbx!ek,\E\Ú[,,h  j`ocI0&I $p3RYybDѰUW*g'_Nh˓72Ps{*4`EqAqg UUԱE S8ef;-ZVt",[ T &Da*u38#pRvm MI@dn' ~'ր(ibd kQDmq""]Bb t{˙ОJ5-wߒ1uC[biº,ySS 9ynw1=+BJc\gW)N31kEm.䜰_@59%"y$= cq#iSvJ%fo,$ar28?_g&tv̍+o͜wDӢ)*KN |K_Irce\AsoEkh%XV8H;?WG8#HVʼn)0sOjGѬ\RrcԌ993ހU{xL8}wjXM>D Lq=u@|THg$v nZgc?(bc$]5EAb3@XC:ʭU pyϾ* -Jw]hZUDbJOcZ)DH'$֫i>H'I<6Y9#oLP0oLNOM0Z@.f+qbp8:MEO_ͫrG KWp>Qb)ح v)b@'RX&Ru<ib}8fAma L ?y@ ͌|OzѦ݅nc%qI ^:ޓmy>lx8{H{)΍6(c/7LaʡJI\sL#mJ],s\-uNkjL݂dr%qTrlڮ" 7 ;*;f+XhM+ ޸QN 簫חw0K]FWGt Wk2[ '<:Q5.f-Y%ъĒˎ`-m>U95hTtV\7{ZO#HtV ¹dR ֪*͕$i/R~ϗ퓓j5;KS$ҬΙ^wyH85—ɨSLY^RGV 19kW e7;~is\WQo;[- !c:rsAc4m`FHuvm-+8tN`v{ ғՄ̚X_Fǃ}k^(_.:ԽZh/"Pݞ ZІQ6qfHͺ֋:<- Ҳdj1sZWeĎYJ؈ރo 41H+qksg#%%JG)A]#܀>G -Cp/`ztzL#_P+]98W$H=* 5'݃^XxT9+1d4/Z_4/Z4i+7ҩ~MOkА(((((((((((??QUК+P j}NI+~Zŏ2 ?ƻ^} S}ka7|(ϴȦthA~u ~rı#֚ ymu6d0{56ob;]ʜr=}0~5oԋE?3q>nAr%q?ҴwLvZƷOdq !?RfxV3<=F;TJ:H&zn;Qu'/Uwɔ^ivE:3[\E2)V q£[,Pm%ļx;_IQna|Z:#5 ;,oM.]Y8 wSWmcÚd2+Qia$ÂTJB"G݁'օzn,# ˜TA-BYsk3[[6rH˲(̬G}X\݅ K!̃ v'*%5rein!fۏV[ ERHLX}-kmeKj_\-YUx8;u6kU?Zei>8fV'yu(#Y5Z5"`7dڶHon,(-~\/Ҩꚝqkla:t(pTd~QʛΌI? =6">tI%?U"fKq+AG׽\j07"Bjƒ?}_R=S¿]souR\4@j[_?=PZwpQF ?P/2gЍX_dHa@@k3X#|췰?~2 i? ; 23efq0S{$Wfǥ@k닩+@;( @a޹x\ifs'mN="'{y G]T|SEH2 I] 8vBT*Z5zk˔63cEk˃HL7Xml$`99W$ɨBL)<1?!ɨ(4@B(0YW%,/tILڎ<d,{f%q_Ndָiяw*Z\^-Ė%  :ףxFqk2KusgWׇEݼG6ەpz RFkvsoSϿSDm2Ms~+P|sJd*=F@K&]cnF9=ɪܭ/Πz}ZZ-j"RX" k2ۻ'TrIvsՀȨ4(!|b1t[W `川 ZC+Hԉ1}IӊǛCKvT&!^ ӄJӧZb5Y*㑃kg4 28!y>Xښt%H둜:P5S% wu$=# OIlszH?eKJ?x߭`rT t/Z4{Kȿ1]M#ָ]5wxL³WC4VAN uY!EPEPEPEPEPEPEPEPEPEPEPX~#v^ڷ+ğ ԿP>֤^L7"_iVAg]FYa\oeVR>KrQ+z?A.|p8 uJ,&ے-&m@7&l*䋫՝N4ڷ1C#iD97s>0ŤX%8Oz__^̶,k$BL+=؉ΌҵVf PLҸⴰey20z1\I3,rq]<3șu%#G^%= YB9K q)FI,~Lu?##ˁ:tpjĕΏě]TM b6dmW%tdιc5O_\H,1ORϬN|FUʱ9{/ dld pQ%I/ &8N>oK*uκbl~[ d{6}V09tcIy tc e`dֻ[>a5ϖs# `VZށN;i]FZ{Vhj,Q(*})E%yk˕[FcaܛXyO G1I$? ҴWVti*:I_qbS{󮂹 Ƿ:)\4@j[_?=PZwpQF ?P/2gЍX_dHa@@k񮚲ey?kW Wq$dҺֵ;X&YyAc vg;))"i8r]#1&۟7q].r GIw $]2 St?Nm OWŇ'inRnB!'o * % bcE!p@ዐ:`JMSr^^ F$12ǔϟ]?:1EfYJOk*hR; ʑ]4RP\/Žh~J6F[3G9o q=hzZt V6. 4'v>P#=;c+4摢F#v4 +YY5)7r+IC23Yvbq bMaጛJ17A¨wPv(BOԏ~M^;iNU{)b0Tg} #lJS5>fGFDʱDhBcjf;]kM3j$tQdiv<)vT2gQ(I%!v9JϓJ *2n:14xEFPG=3Ykpyc8onevX ԓYFep!*Qݍ^fɉG$+b-?̖?CQ{dVKhA >aۑ]<,^#2W$6r5 8(co峘U;dy}+;Q u n ֬ frybic;c4%{"B5#+WOZN F{VnsMgu,myed<: fFClF1j=]souS\4@j[_?=PZwpQF ?P/2gЍX_dHa@@kj>q4^^eqAuitՖogL(2gZ&L r:'NMVhK j u\L,0Ccqrv}6XsӃU\E{$a00Mfܷн=Ҵ@g&WVs7_ʓf(ʬUs :}±p9e2H$yʙ{vۀ$GҲݡEenCNvOv&U%+~i݃^FV/bF9[ Wh@/SIX֩h5j[u7I2A?]!( lYاo,IX=kzKݓ3)VAN ( ( ( ( ( ( ( ( ( ( ( x𮹾%ۢta=S| 04zrʝ9;+XӄKG"F=Vn7ox?.bݿx1/Xam&lh 5_AYL1 7qbI$bY|]F\o4M=PxL0q`yv)-fͩƬk411(d2o!`K;WP/RQu$Orvk.\p@^ԏhQ$ܯRa4yV3C[I qM>+"#!P?aZs] )]⼻6f$Q081nt-*]1j S]CL4dRmt=#KCG'¥o;i?B*/LŕU7P\H: Z^?WhF-%ڊӭ2x\ XƩ^cuW?o?]3of_ K\4@jNt (ӿeO^L#V) (P(7 LAEPEPEP\o/-](i)}2yl1eWGA@>xO Hb*٣4;U F?B>G}.c]M?H>x@ њ, ??š<`GZ_5﹣4Y7 s OS^06+,њ,>|?<^F<>+>x/Xc#1;K?Cyo,}Jɓ?TPʴip@j8xjT/'Q~?]-ºw ï5@+пg|s5@+пg|σ_OKEs_|BƏW^~?]-ºw lMyk=2Uz꾡 =#s뢮s>G@3Hz/cC~5E:iE\S'vՊeO (X}fh (((U_[Jk([`n)MƘRܜN0j\狮X^CDy9dބ`n Hip:Eel"FrJf`Ā/~m 4Q\mxRw[4F${Ԑxӣw_yUGst'4\M5;0Y g9 g}⦴$8YDSԦ9'jb_kVd.T(ӟ9sonُ9ϭtQEQEV7.>ehMdP89ckW5R[PGPc{E#, #q)VAN (((((((((((?FЬ?FТ(((((((((((#V* ;S룮s>G@3Hz/cC~5E:iE\S'vՊeO (X}fh (((__ZZWJ[-l2\iCJb.vc`J<Hǽ ^Y4qL|"|g>YjvkG ||q +C'>ǘndsϓ?E'yc9 a)c#ɷ>Am qUh_o\Tg݉ \OJ!UiViqHɄ\c>*̞%1\#7Ew4d0MX`hz1~V7ݳTvq3H @ 9ֱ$/jtXE%w ;0000}xJ.i> 0)HȪFNp309jHn"QyW(Q 6O\5#tHCΑ`LM1#GoZ',0ʑ3N;Fݹ{gz:m*i$pHWEٳ?wZLx+,Z-#ʓFӻ 9~ߨZãJlBf1FA\`kDSG$M Q&Thp a=jA5nfHYdv PI4͹UELM\*M7p\1f1ֺSdKfŒSbB$'٭jVV$-ږ#݅kuKfKSWuHoz5;GO0Jp85i|0oV1v8vP)NG$gPy<lSC<*Tly@h!kQ濕YlyFTf濕!kP*Tly@h!kQ濕YlyFTf濕!kP*Tly@h!kQ濕YlyTm *[ ƀ5>?цLC0?wfwxW'נ ( ( ( ( ( ( ( ( ( ( ( :5b/s?κ:7!RDAlGZvomP˿?Ue*x[)Qس#N9Q@ ]X.?TPy<˿?U-e*2z~KE'w=aG̻ߣR@ ]X.?TPy<˿?U-e*2z~KE'w=aG̻ߣR@ ]X.?TPy<˿?U-e*,oDQ zR?Js`S]%s^z5\4@j[_?=PZwpQF ?P/2gЍX_dHa@@h}fb ( ( ( [ 2/YʐvPlVdk!{yZ2e!w GVyk:嬢8ڄrL鞝m,^+o2PM"т>)$qJnu%!p'z٬Ie$ ^/Z򤈕bϵ8^-twrf"=kH+gI9ȳ_؈JWx75 +˥]jIuh(OP[z|7Eyл@2 ,_\ YWRWLoU#b}6m G4=#,3&IV}WV(.o/@zzG'="hрISWh@@zT{ivw,;Vr)meFۜ/z@LvxwKxถ2L8'Yhr7ZQz QV*)Gɥg hv5bfFU، GWjsw@=^> Z5b 7kev"<˵a~/Tcgdmʺ3_bjwxO\MI$"`Yv;=+Vx\ 2RQ}6Ek9tmJWBB9|-Z)\IJy+ `d sG%dp e3q#mD)0tϦyho/yt#Rɧl: ޯw*vGj_3@瞧jq^ hxbR@˜G*o?MDaTgKΞ԰Ii/`ؖ IP?/S".?RC3;93Ipx+Z [ki`YVFiVݴ pMtrȑ H$U *&:i>eoFΠta\x5uilۑ$bYL0ŀ+slT.կn,-{.YX&"C/?BT}*?7<tKd3 Yi '$_꺞aq&=̱~``8zհM'x*ZBga S4FFfe&Co_Cxgc\ždtO*ت7fS2\ޔqWBts +7MlmW_LڍMkvpWv4Ct8?w$V:@dd9#?'hHӺgږR9nzezx^&5xrVX}bNN+A^ ?saY!QuY!QulqI|su4'?M`k4PyѼ|Z(7FMǣ&MKE'?M`k4Po&ѿKE&ɣxk4PyKE'?M_ɥxoѼz7ih?M`k4PyKE'?Mǣ&MѿFɥxoѼz7ih=4o|Z(7FMǣ&MѿFɥxoѼz7iiӂd}l@=4o|f/m PFMǣ&)$c?C֠=4o|B30Yp7m PFMǣ&(CѿFtb}!pi̤N=?`x;<񮖹ǂ5S\4@j[_?=PZwpQF ?P/2gЍX_dHa@@h}fb ( ( ( 6|m3=krSKʢ78i98dʄT9k6pF{{׌m~ȖZ{]>GC9q9b;ؤ#T,N3Oh# #%<|T$zupT-lbI$IaR<8jf,=mEImYb6p`z3n^?J>VB.7X U vu% 9//O 6ۺzKMb5 9U1: džPh@ۭ7GZ𷃌%~ A+qjz?/&SW*GcݱgOip~OJ1aSUO w$ۺ X)ĒgRXPu$AW:}3Ldۿ|==:W>7Ǣ[Q$wcRp0Haν}SMJI&ZZMǨuj7SINaɤ!E.(%RPQK1@ E.(%RPQK1@ E.(%RPQK1@ E.(%RPQK1@ E.(%7ZH؀pCpkWFsk[W_Np@׽sqPҞ.Ֆ;(nV#ds⬷/Ŧ[It&'+6rȭ(#mmҼ%(T Kibl|g1#&_?o(YR8rHc댟+)%B6~(ED2!\Gc΀$Kr )w/]dUCcz~:Z$N h/3 hbY_2u+3e>dc3:]yPB/2gDU7N0VƢlV6%ݳ\ITMT;~v(X tWޥ4lc# g҇E8c}$2@s -Ȍ9Q})~(x`q җ@c;<񮒹)[3Hz/cC~5E:iE\S'vՊeO (X}fh (((SHWW\mv%ssr6Ҧ[<hOxMO-Xy޼煼i7&QF2<(c׭cAC7y5O+J1LZ?ճ+L4kj3Ie ޔ^ +zV-EAu*zw[TڂnNs|\g˻8Y;ohj2m8G ]RӶ?t8TV+gp89<3w3^/S,28ozOrrkqn=掫 IT$pX3Lc&B?)wD1i̱GG$ksZ^iv:.~bvrg9隵ZlN= ʤ9W^T*8GTՅe9xXnt}yNqI5#ٽYeUB23ǥz(ӽɭQV }j.({Ej`s麹rb.zn5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?5 ~c+9s ?u ~c+9w ? ~c+9w Ow c7]?—;]?ºj(/q;]?ºj(/qEk!y0ݏ]%KN[8BwPm/sn0Oq*~H ?Q:rL#V*P[&\l~G) @6_?#hP PhXxV'ޏ?~S=? ~[(OEAߖ ?to@Ph?~P_U[8bm7{ʅ8GO-TAnN_Ґ̴iSEcc1~x)+Gr?z' ?!?P'ktӼfpKv,G3}?NֳG' 4RJJ ?{Xf- DO19}ExgEPK-7]"YK@8Kg? k?O2e$V>7bI+ubEֳG' HDBO-J Gn骑,hE gHcK6_?Mg? k?O\wdKM \8mù浼w' ?!?ROQg? k?Oyk=$gkYBZ~?hk=$ZI?1Y(ֳ@>ZI?1GOVw' ?!?POQg? k?Oyk=$gkYBZ~?hk=$ZI?1Y(ֳ@>ZI?1GOVw' ?!?POQg? k?Oyk=$gkYBZ~?hk=$ZI?1Y(ֳ@>ZI?1GOVw' ?!?POQg? k?Oyk=$gkYBZ~?hk=$ZI?1Y(ֳ@>ZI?1GOVw' ?!?POQg? k?Oyk=$gkYBZ~?hk=$ZI?1Y(ֳ@>ZI?1GOVw' ?!?POQg? k?Oyk=$gkYBZ~?hk=$ZI?1Y(ֳ@>ZI?1GOVw' ?!?POQg? k?Oyk=$gkYBZ~?hk=$'?笟 ֳG %>Mh`d'WMWOPIr)•[#tY,Fca wpQOuH!QLGfotoxx-15.11.1/data/images/warps.jpg0000644000175000017500000012214312616075370015675 0ustar micomicoJFIFExifMM*JR(iZ02300100Fotoxx:resize|.Photoshop 3.08BIMresize 6http://ns.adobe.com/xap/1.0/ 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 160 281 C    ' .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQROC&&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?MJ} NwY&fkd$$Ut&kqrbeLҥп_?j=m`[UF&G1x g+O.mScMH6'OϬU4%˪N%`PrABݺ}v$TC@e 2e 2 vF¦QU8ɷaa%Tд#qVdZ5ܑJs{&;'QBRm Nqͪ?*_tOb;V@d (Ɵ W]wљgHc>k==I iPmSҬybU1Z[s>QXG=hqI >@]7(>@]7+J#-_4ʛ$qjmĦF`O?& `_D@qѓRGgHB܏ڡ4hlc`EaOaUQ=l݂RL$TCi?µRXn]bVi[.A5 ȪpT}i?Ÿ%>+ʴc-"iF|$F'>H#2hͤW/eG"2*i1GI|?>h-ʮa??kc?feQ[\4a?=s<$-zOƏ='G}"n焟eC2dsWzOƇV4 a;.߳`g?,@ΤH/#G`:J>hi* Jk+*E( 0P[d+Ҧϭק_a{5܂(ϔ&zuTC"V&N$ Ɵ>h{?2.urp:DMB0u ֭a??=P&HYU8=) a:>h4{o ~ec$HNL AW?zOƏl)j1HuSB&P:W?zOƏm̡%^Hv=Ȧ}kGI|?>iw=g}}i>w>5>h4{w^cy?>w>5>h4{w=3~w>5C$ve%VF3;q='G@_W%bMEܷuʾsu%"ZBHYTpS^Y{2iVZw^a!^fi_/0QxFk8Riq>`#`@TεฎIҤSl;xz{t5g^a0.^E &Q^WP階Ų JMxֆνn֩6,SG8W=8)bpa YEx$%*6q'hRiڭ$ʞ]*{:P@8:Y;y.bЀx퓎lT=@gb?B*FQm\ǩGiqcq LL* #SGZNe Й#䲎8xc[9@b."{¡ҴGK{Icid\͒B_\ӵ3ys=֓Z@̒~Vr Ԧ i%qE=k;@Anq&k89^$]@L ڍΞnO (љmr˒=+#MV@\OszնF$qUCH ֕/kR"yU܆ ՟$I>~*F#%[xORQ?j$aCq']I-nH.P%!3!ᗧG@hz˜$[i勎N}V0 (Š( ҼCm`4#{5̽`EZ׷}w(=G'rO+/fgvQZH#_jskr[;2V46y@mF;Ҡ:ȇYn5FcxdހIMB 1dޥ 0`8 TVjkc=؝@кM>{mCq"XɧȮ^.sTEPZӥw ͸iaPO9jvJ#Dc>(APj;l$g:tm THᇨ~_H?ƠSM&]-{UX.b䳍oąKpcGv+#HZ;155s`<*ZeD\qQ+mE.qc$r+DcvB7[_ĊCVcj4Vp(˹sː7 z <\-Q/ҡ&0FeÿҁY-ťm]hdqץT]KG"k.W&[#pp9@TTV1]%ŹfAbr=pFqRǹූ%#YC$kV<*e?GQHcK<9 B9WYӖY hb26>1I|Ao&eec'e1J[mRNA5@W_ox{i^@̽`EW{5ķscpTG#<+e*ޤ _5rXm>%G*,oݧhfUem!$TvZ)rw^Kn ĐNsIR@iUm#$sE ݆%yG+'ʵ )4Z{+hkHFrߗ#8} -倃Cc$+ :%Ioj[g^eX)NեafFo6GF~z(C!k4^trU`ޜc̡@suԔSZ$Jq9x[c%,ٞ6 6|o?ZۢM[ﷰىfg 8ZU^]{si{ i),D*~Q}ȭ)p=EMu7ږ/*9GfuVs&(`iA" B3Z\4iS Q2 8lԔP6M\ W l^B6ӯJki-6Gq;OJhg)m{ҷky4 fbM$pm0UKF`Mkr##*9t.+;(xtYȫdb(|/kuo53J`UEUGUcFk w#OJ}殼0{t/It(+LƟMRYx&򆍸$k` tTSڌ3i_ +.6CJ:=+e*ެ+e*ޤ;Jijy$Ĉ `d ׬-iAUVtlnWlt|ddrTsT["Huj1d`6)j}zd3f6EyLcB+3P#pMvsIuX+m8JK I"#dQza"Qˢ^`eF!3 A@Ի>oofS[$D۶PaR VٯŔBYd!XqE % 9i˸ݵ 5{ҾtoeKhVbV dU(G*}PSI[IE+JỞ+Nt<#fsZTT7W1$1FNMMQ\[s:o\skȩo<f{?4O*O*ߙ{?4k9٧dTdT~=h׷s߳Oɰ ɰ ߘ{?o<fa<IQa<IQ0Z9Y%Y*=>I ^ޅ+OR}㶃tLۗ#āٲď,(_ގ6Bk7Z%,Υ7 K3߳MKS<[+UY=5/ح'1oO?4}?o?UbO*3߳Gx~?VݓQ+oO?4}?o?UbO*3߳Gx~?VݓQ+oO?55pmm0QLdU,P H ێX'u$ ͺ>_ +.6CJ:b=J{@J{n|})?_j>D uߟ??_juߟ??_juߟ??_juߟ??_juߟ??_juߟ??_juߟ??_juߟ??_juߟ??_juߟ??_juߟ??_juߟ??_juߟ??_juߟ??_juߟ??_j̺>_ +.WzW ?UX:W ?UH(aEPEP]4gsQiȲ(?S/5=V7WiC+ dp0RH&O*7UC,^;2X=_:;or PMZTɯcJmS@ yĤs#?4>%]l v( z}iss YbvB ýIQy 8%J$T@i׷$`gw dHc\όTn⩶ںk-ݥΟ j=V?Ocp 0]' ;ʌ=qLW"q>3IP#"f]9A4kZ\J- 䃂?:ЎA4eE·*jS`Cv6ހ.M!q>3IQ|fkvW:Gְ)"Aqh8sԋib}nm8yw}1 q>3IQ|f/uL|)8V)ߚ5 6TeP3AQE  %FXrM6?@Z$_m]G4?o?7\όTf+[ŲQ}X~u'7-f ǭ1 |fu}'N~AwzӓT{m%-"P:@RRd1nz*޿l,Ɲ4__!w0b/ր3n6CJ:˯ Ҽ=J{@J{@QE ((3V-nΥg"M51 4bh^"pHE>a(kv;^8e B$¬]躅:B\2ڬdG.8 VYᢷ?Y isy<,c2͕Km=J{Cg?AVmeѥeM"i/OcүfyOMhfJ"D'k'>hqT[v $pIU.7$Fbϥb.+ ܄9!(^#w&9C0873y<#o k5";19ᔖ9tlD@Ts֌8?hHyI $:Qp9k'QԴ{ _ˆA`EPpz]Cċ Kp:d`,;T-+^CrJ!D`9.oxqh=֪@rqҺSh2~ NcKnQ65:[ydÍ 4~K g7`]? 3~5?M"H-g8W#怰n.5q-uчi/#nltxERFܹHfm7ZڍGs$^hЩ8 s+7ZJS <<@Um8#5yOFo?4ւhȺn$i]N+ᝊIcj1j f8A)KŔa&8?i[Me,c,WȚJ%Xv8:I+nc?V?M,QfRIןSXu!|W׾]m/tzW ?U\KiFMy-.-<22H`{oMn:+ѿqAѿqA (_SFoSFo4of?4of(_SFoSFo4of?4of(_SFoSFo4of?4of(_SFoSFo4of?4of(_SFoSFo4of?4of(_SFoSFo4of?4of(_SFoSFo4of?4of)q9ǥsښ73?'ssO@wCܜ˄IׂWGӟ F G8?«:Wfotoxx-15.11.1/data/images/edit-geotags.jpg0000644000175000017500000003431712616075370017122 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|tonemap| 4http://ns.adobe.com/xap/1.0/ 175 462 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?#@'j9/m>T3JRz;VI!;Rv_ҹvHz#ѣ9WrG҃( GvY^D,Xq{bD[xZu9eZ-Tm# >*yR]=דQ@TqVu!( 'eN=YVbY/##TdbP踬X?*B =Eq>$_ka<ǞN*]FRU]WN3$#܏}.;]d .?*TಲhIGu])lWaG}Bxf.chgn?ri~T[u mWW5B,5MGz{SZx41<6Xe13<9x=v{wAW!xTԚ+U 6wɸuiz׌oH$SmW?&c=F?*m?*7ATEp oOzʍ~Uz7p'=F?*pAލ\ AQzʠFz.=Po}~Tn7ѿދ>?*7ATEp oOzʍ~Uz7p'=F?*pAލ\ AQzʠFz.%Xʤjm@+?~Tr61X^i\!J+bkQbjW|Ty%\QU"?俕D*V <ʻG+8xWK)/-/t 1g'?wأ:_ݳTңBq95}nTErE„}; _bMWXNo3 Ѽ}&py䊱q6hk3@e:gNW\$Zko.nR]F%"DBx׊?RXUBgw؎ݽsdCo\j/Zè4I,%;s/엚d76[^SYXv}M֮23io;[&47iu*dg5s n?iSpA9KQ'7de CZ5;uwj䥘P%) s~PH}07"+8/t:1rp3pFM/Z554sۻYV݌RmlS1cJN-ޗ5żڄf%knϵ_qeHtnflcj6gR{y,!DN2sqT֯5oEԄih$NrskoeGZ5ZTXuͣ$ړ<'c@HNsҪ'MZs}#anc9._j S>nBǗ0 j⩫;w(eGZ5{bki{i&iZrj^B-QHaQ=Z'ݑzԴPŢXZƌYP¸R{Z 8'yxԨ$p1ִ(;[=n`cQSRaБ@ Oڗ:鐗]p8)Tx} 5bX]áǯ[{xtW2hl&.f^$mtI@*##ֈMbg 'ŧIHo" +xޤH3#nrq8jO7bz~$h?Ə-?Vش߉??-?O&,iimh/oO7bz~$h?Ə-?Vش߉??-?缣 e ?Z[?4n78GRy4iimicxtyh*B^K:b1dcxt R?i,y+;H+H#p"QKE@Xڪ` G=m˭!yec;h&c^cUl笇e #򬭣زlFzG<,F(*=MKe=%y??l/8`}{K8%X^!l礿'J5{1YD>o;O LzD(HVPFȘ(6-?O&O7Mh|qK!%n$:s̘#ye0zlZYğMo'@ ῳLŃ#z楖Hhk) m gWcՐ~d : f2~qa}U¶S=Xzz7G?s'\T+M_@d属q۷ݻ=sV ] nɧPSyz#h1tǦ{՟1:<8o4xV.l~$>S41:<Z[?41:<?t/ơޙE`l`ҧOZ(C3TRWm785uz%izDSV;\ H1@Ʊ[Xzא6CCdHYeWc(Ag9Jm%Jצ:iUsք>/3MK4.dVr \~q[]ŲFVz gZMG6dSOj5;Tm;D>ЪOSއί>9ݥmT^S0)*wRƟ.,GqARΏ<"r7i4 fQ[E*?$V΃iZ.M9yfD`>^p@Fna3\U-OguaqznVV`zmޣln)m.B/!?aҹCts\C3ċm 2 o=:3u[mkN͜!!wJ|QcZC{$ʱ;GlV5oh<bnA6$ wqުoQXx^'PAUK/ڱ9-|ivEX) (((((((SLe@,3PFiqI@h4!taͩ˔B|z)x{cX`Ӿy>E. . 7m Q)5vqu2C cs'hh{oo"^Ͻ7Kdޗ EC}}!v 4 o/?v)\<0K:$D/NzHeye. (:}b V8ˬHzP iĀ ['K@ 3F 4f]/{$cxC$H¼yA?*w/?煱?J^$ 2C=zm Q)=(7m Q)I4,7m Q)piphh{oo"^Ͻ7JFA=גDdܮGJ#c_}-QEQEQEQEQEQEQE|[o۟^6SZ٭is}shdaN2hZ;Q,/wޔ[Y v„9Sluq un^,$(3>7{z:Cj7PjTL EF>5Qm?ٵKy h_H[ѽM1omu4:~-Ē!g "zUѩ%Fڋr+?LwM&h[=5HIMR~#b8WךMi$mW΍?1ˠXϨ-]L/I91z=$>e:yglB1,rw}Z9_g3?Jiw_څ֯}(K\`uc ޱc=;Rꗗl9.`l` =+ަq4qQ^k 'i%SuLc ][ח:L/*6ZSF43P7b]Œ[.ˑ-A-_P}OԮTiQʀv>k,ORhùjK jW46vx6|㚷7/.F5(4%"2:wM.45kY<7}^r zQ6ߘ]E 9Ϯk}M.4_Q[Cﵛ1u{&H{ujs=6D t,OwPW*pF} Utm&춡.$43uZ];gx"%'Cq~6o丙o m|a=? 7Xޥ-hgC'lW=u]ǧ@%Skwz.F!]\QT?eUJ\Fd\W E$kX9v=Qoz/۞3 Ovح/lFX\3?~j}KZJ9딁`)?kn#?.jP{yVmy )遻tke),rL׭8X?U%Fkf?S{Koƿ#եNa ׿NΜ72jZܲL,$J/66)'ӵz4A<tz(">Xq5%oVJsr]B(3 ( ( ( ( ( ( ( 1HU[(?QIأ.?/GbS|\_ʀ1MTyqq*v(7ˏQ'_ʀ1I_%?4J5hP5hqF)\_ʗC,SyG*\QO%?TRf;b|k|Ry)w.l,J䎞B}7O oU"5 wO3G6adcqjZi:Rj[ɢI̜ҶOH&o.9nPaѹk![ƬM+F[LF=:)4USQ]G3kh@Wk[+yO&rW!}~Qdѹ ƼIu&ҿqv29 )?ijkZUX[Ok "ԣ86UQؑTI@= 6IleUe 4[4M >ҫœ=-u]f[Iῒ(#>T͵CnשC9X3_hD$"n+’)gJ+B rߟZl~mC PAo)"ZY xm,jA[ߡōH@"ni.KL@횭%^C$I#I"r${p:,*-ي4<O|gC'BèkPq@ }-h6UdڥiqgW隝ύ{)qr ` G+}.KA EVqO[ߡv] C+]A3Cr>IE5n-2(sO6(R#m wf>AGy'~<-!C:)fyP92 8NKRAg*(>Vfɦ8B$dDPKW g%Z;k>fI$\=AYϕQA[IsqmmQ)gva@ҸUu$$dKY 9 v-杫CΡyj9i>ԧ^Q#M+[gE " M|ﰪz~jMc\(- Gٙqc{l`2NIZ!'ˏqQ$I:[]6'W2n$<[U֥k%֓H%Eȫ!HgB[Dl2\0E;j /C+h[UЧЬRy%l pzd~K̷O'i9H;1@M0= _o~TWVK ąm9ZѬkGI֒ ҼbzumA.d}͒:p=6? ~B,m g9 $wWs\77>)m΍Pв Z. mGhaU6! ew5s4f%o(w4iF?ejis@ /H%k# ۙMy*hErцw20`qQh }Uњ._&J1 /VFhgYݟu,1@Wqڧ'/:.Ҭ\Jg} w 'UњݻYplnm/&,aao(۸`x<њm^B2 X 236 300 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?R4Q9$uKwGk0Ϡ0\OD+޹㷯ֹUT:wUXUd0r(iNUɮKZ]Ge,w܊b,2JNV=Hu9֣"t$ Iqsʨ2ZɹI: In^+ <&ĺ'Ppce~UwoufݙfFVFFn rbҀ-n>wWVi! a2åsWSe+Rq|%=T\G,aRP=MqPwk {d̒0};?kZmmo ZYH+HD:E=F?*<[yqamfxf#ӨAuF)=YmSQC'r7AQzʹ_]G9BKg~PsLOIpcllWo0tv!Չ T88F?*󛉞ŒrSRP0`eGPs޺(5RVO5ŸCW'n?t=\}.uKgX~AU'ӵEkյ/*x#6L۱EwAQzʹo Y4"!* ^?JKRKn1Mi~R4\,u[=\4>1צt6sda/UN4}6;Ppw6ދnprM봲M64fO9UQ9u!M!߼}@?*7AWw;[䲎p̒qdw8tŔD"&܄\=F?*}}~Tn7Ѿ'=F?*}}z֣o,s7i=FcG"Jә,~߈ZCH}%=*ҧL]#IM3t8b1G\qH}7N0QVDΤ.iop;K mV 68ʰ"}F$>YteCe'!@@*?ZH}7E]%HI2psލCOԬ൙ r3V!oꏲCa74頹^92t/ iא}4pO$# ]!oꏲCaXc * l0*^%Kwyq[^ϲCߝ/آo΋0  Oz 1B~Sq=Υ%~bvb(ߝatsӮ%ʮɅ@ ǽ:]&GIp?c _oΏ r:}1\^۳0ٍ7  {|9Z\ 崸yf&4 _oΏ]^_˾V].g.EVjϩ;K= 0۴޶~_1u:,FmՒK[g.9Fs^j]4ZYs{ ’E0We'$1;K(ߝbffHZCY).pc*-Dlxn#(e$]1 Mfz,Wv0>Fs9pAac Np^$>}QH}4+o}Y$>}QH}4+o}Y$>}QH}4+o}Y$>}QH}.1dѿCgz7EsQN*6[i"1Q|B𤬳IT, -i֥;t\]  cҩA#i0k=C夿h%xysPyF42_\NP8@P][ޣ=2b+i9.u9'kY#eP&p^*\Y]C !MY|2sgJP+.ubnF1 sn@c$p{_6*_G+.\.3|:i>wg~pW]<J~%أ|}5l Im#E߱tRA?0Eۜgq\/{ żDBq׊潃Z@!#qTKu&촛+(m̳(rfp~ BP7iwRb;J$ kQ,,YUUt6bY3/P#REy煯.'Q}x|8cϿiMoV,."wgԐv?Z?? ?ߑ1@z>Ηkd-8˴#*C*7[{#jP6$ŘqN}s@s0O?š'Jy'YJ~o Z wZcׯWX~?P/vToQ#SתqVuhu)tm y \/[k5B쩪N.#Ci^-|HH c#q"GH>&!((((( X=GKGJM&ڽ8Qx6ql&2I= Σ<*$̋U 'ŖVoxncs0o_$1!Zբ))'9qmτS}Fee\z3jjmY[\1 >E W頋|kI GhEӭd3ytoM$˕G ~ں#HIk4n{inDqOjWTf( z> _:YKu,UkQ'c@' O^zR>*[NW,~MmsqZBQ(7& ߥI'-'-RL(1ҁ7'6-567`ӧ񍕸[O|y 8Adzw-$ϟ"vUo¶b*hhEedF!ؖ`8$s0FjY[Kȼⶋs9#L%[gȁ w`}zTij\Y[L"Ki7RA%y ,@5.5 =X;;ޣ-m/$Tc#= QuL:Ưgn'NƬ=?t^i4;kLd? PÒx-j2ZG hzt9 ׏1.G!2|RbѶKЅ2OC v [9e }!R`1 _IIZyRmuƶZ=Gv`[~!j.b3BdrӜ~F63KoFɃ`ztgճ}-h=j6vaJhC ( ( ( ( (,-T1 7ZXNyUM;Rȶ,M8늹z J(qKPP(#h%dJP[YVh8u@ !*t Z ɦbMTB(rx,zvZL_,U=-XBli?4u=e  ‚SXz^<^<Zǥbӱq]a?Ri[z dj}( ;/cv2! IʍCC">%8~ݧi+MCFp>&CCNX¤+^6UNR;|.FÁ\Zh!6V c8#yQq+ԭy-G *8Q46Zա|pȸUI'I>֍oR`yȒccKd!͸S'sȫ]W;R5LK@P;{(ɠ Gѯ`tCuޙ<͢L|u_ a<X]#!6UnN 1WPM(bZ.;oR$z5+ɟHkY-4q(#9ɮ"&uͤ@TӞgI],6q^:_u~;X9«i/ߣMM@ٵqٙn/Z4ڦCUQlXง6K\NFEsG5i~"˝Z{6R0f s=F+&:Z+$vb+#p ݞȢ|Givle-jYn|W٭픬ii8( GրуYzmA{Cj @DRA?v|Qq<"MŕK3<:OjuTV'o \^9cKV MQE((((@DH%5 h[D͑F>@_EK.:U/7i&5i%z=FzZNdڃ1VLJ4UkqE3*´E].lXĂыAVOR*յ1 aaѳ-$j°ƁcUڪ:FMH-$ӡx,dp jSm#Mk8l͔?gđǷXt5r(TW6^=K,2 :*Z(ic0d^4}G[4# *꼂zօRGhGt]ğkEt}1]l ԁV(=t femēY_PEhE O=>Egh-oDŸoJlf8atG@kJeowmIB/1F})/ Q/"rqVhC V$0FF 8E(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEUgMTQgˏPF)\_ʏ.?/@yqq*<?bTQgˏPF)\_ʏ.?/@yqq*<?bTQoV1@l:6C?bHtQf}#d^F)\}RI<bS6C!1Ltl:~(3d>ѲH }PEPEPEPEPEPEPEPEPEPWRlzliX6|s&GJ#XruUVcqG@C])kh,QKa>6¸RuZ;m.vȑsV5n+koQX^W Kp6@LGl,,ߥ ??/WǬj2fSm^а%*eq15-gVOE$Y]T dwz}ߥ {mw++дC=JֵIc%,pI+x5KU {tnZ ,NĈ7Gq4-ݠQ[*1zhɉd4x~FtdZ鰡 eQUIo\Aog*z4FkJ>ݣ{1LQ (,l]OVѠ{{{͆KfkTݷXjB;3a`3o~(ϰ^{ajn~e"?ȣ9-gU> ā5ImoaRvG?0`Uц!F?7 3ָ}kVK4n^)w$y3AVR$>ȀᏘyg:PG<TNqҦBgRQEQEQEQEQEQEQEQEQE~uh1Us<2`e^6ERֲ}v7RlYg> Ѹ/{1/Go^~Ww75{!/X?Ww7.V%]6. t ֥ a܊\N LI=O4f)ZVC4mKe??[])lY8Gş®n4n4K^~ ?I{*F@Yyhm 0HX4Pfotoxx-15.11.1/data/images/geotag-groups.jpg0000644000175000017500000005425712616075370017336 0ustar micomicoJFIFHHExifMM*JR(iZHH0230.0100Fotoxx:resize|tonemap| Fotoxx:resize|tonemap| http://ns.adobe.com/xap/1.0/ 1 2 3 0 165 758 1 C    ' .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQROC&&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?,,幸}A;U @Ws:Ss'L8봿VOo #E`>UsJps*5NVkH3Ԇe#$⫈g}_suKHc94G&8؜ogDUVVK๸h؁WՈV/DUe'X-DM(/:6 =VIC+Xc?"O*pk:i4Ѕ r{A o^jZE,'/60ByL~$,?߸?uWQӢ9'XZYC6xjHwܛp}DUd'Yϖ=f{{ y;gOXqT}DUsj:Mgl6mҙ<P:c׭`Ҁ:/~$⫘Ynb)Kwfh8=1I>K oM((,?߸?>?"O*ܠwn,?߸?ƏiwQ~$>?"OFOh,?߸?]nN|ۥE̙fܭ̖r^ܲ,0c)-lxjk)"i'u&O*d[e&?VxrK $zV3tҡLPnI=1q'HbkO*Œ(0cIJgHWr {VYw8_1T$nu@*u! n<49ۦ#02f瞘"١IR/\DU5lW\!fI!u£ҸX~?"O*$'72Uiײ-prΌOG;}d3Z=̖'0;/z$⫓Ե!fPǓqבNP:ݴrKI +;ހ:z$⫘5K yKďA8=)^Qhc(ĠbPv?"O* ;ik$uuڃkȻʞJLFFy4?޸?>?"O*oz$4_޸?7ѾEz$F>?"O*E}4_޸?>?"O*oz$4_޸?7ѾEz$F>?"O*E}4_޸?>?"O*oz$4_޸?7ѾEz$F>?"O*E}4?޸?>?"O*oz$4?޸?7ѾCz$F>?"O*C}4?߸?>?"O*o~$4?߸?7ѾC~$F>?"O*C}4?߸?>?"O*oAC A__u댟(H?鴿UmJ-F ~UX"'gL:^HNY|!'L:2'tsu54`*lRv* CV~o<G (Tq>#n88*[?xK.zQ0G~)gb;5qC4}yƋm.v*,qڭGi[=I˯h$.ºW$:wmo<Es5t=m['͸$F6)}kS4}yƋ̙4.XcjZ'Qp֑PC]'mR~x|emud];0h͌=] Xx|щ猟jEV&24"td ֬~?7FDһ! ,#%Äw;?}Ra^zֿuڍ=XwK}2ඍ@H GW) 2֞a%A(u|Ζ].Y66ziYE˴?:}:࿝n gڋY%w rr34FݗA2F=Ҵ}g1`c0:Eiak]湓̑`gA@)i) (((((()i(je0֩[f_ ڝFcA8\~4vPʗ̘m :[A B#p~.jΌ+)A;꒙lBqq!Rd8eanYd >Ytxݞ"$1?{ך#UimU?*umcNQO+UHe Ac-JpcSJnj G@0(h(((((((}UEl+1frpj[Bd)-LVD@T2:˰Eͱ '2GҒ "TY2ϓF@[REVH@E$kvE e1WOO5S{QrN~CWݢA ;rt>槷LŧٌK/q;d]s;1e!>,کdMxێsto EGIG8hӗRfr ~nvn9bMVF [Ba=E\K(L%s޸~b5-t5?m{SZTQEQEFh+X@țp=w*YאM[m|nZ4U>ŶQ@UHHJ?  V/ huxyhڲM)'ϴ],slDi4y>t\HN*คzyؤYcU ghYvS1X)"[F`F,+,ncSz b2y~joʏ:-T*Haxi5m̮| *P9e .ێz  m rOf<3"7!vT,"8Blj['ҳ&f{XJ7`zilMY$dcdَ~i؈,$Lp1w:6v JF/1#j(C ( ( ( ( (uVWGZo! 4[SUKaFKP, ~l~sU|Ѽ.;Rp`w&A<}=*=ghICq5[Vɉl?b+>iKd`w y sNK,$D@ iͩ]<>DPt0Yݥ2IR>_>5]m u 0iDo y?6:>l&vWXd+99JPK[c FuK&z3PZ|W&2uGehd|A9=8[f_&Tire =+M-$c,x^0qjuim Ϸ^(X]hEqrGlfls9CTI1 .^4W$9$u+U.mnE,EQMI^H-.Av8NgZMWK&U2Fp߹Z>5ΎFo4*_ZeT{Vn}yܶV k 4.7VJkˉuj r7 UG5EPEPEPEPA祷i63Dأf6+"}2U3dpWo#E[5] q;$yWQfHg{pC^>Q„ U"blx8>%r$fhebA$ӭ.~,W`hc }1H0yR\|c@ get<ȱf$[y9S`Ce6Xlj{Kn`XRTpF* OR$1!@w.A@ Ւ@ cW.0'?ڬk5)-Īy\uZ4QEQEXKn6Z&v*0x9m "-#W_&mrs@d\YN2kV[AZLhXT&;۶I䟙hO G^- d_K3>Mpm4),`+{5DG̟pӏOQ*:Ӥw4U=&lDXB-ŬZ!@f9է}Q6Ӭ"tO&Б, i"C#i<%!2@)?.hæA'Z̟Zd0H챤b],- M CLEEt**{TvqyvIGz5yLk3y&b0ڴ-Xn^cr8vs2-g.򕶘XP#;urx5XfP 6rmj[4c&I;ǿN=lX&!<5ViF;ebr9縩4 b5GE r>Z@ %1n?ŏy紷)']$aI :E0w9!Fs; EQEQEQEQEQEitԜO =x#Qڤ]"1j 3^k}t'{PBڏ5][S\7 :z_KG7^]KR}aa[xQ YqQӭGw=č S,rH\U s2@`UՠhEoYm?kR? cCB׻KE !hݿ% ns3ƮfO ]5mx⺻/]эSJ搫(lUlōûsd61j$MŽm;1{$٤Jۻ3WI_/QI%,͖^1*3uv zOrIH^aqJ(Hz*rFy{;'yhtU)ŷh[2$i4瞴%^_HĒAj̴ۢ%]bffhcw 6 ĈWFa- ` \SZP$Mz,"$",7'G7qvSR9ɭz)ΆKk 3eHNܞ9M:C2-4F40-NK~uQHf}Ɵ+]Gqgp u9rǭ@qgϕ?M+=Ƴ -PN;/)&C\i~Un{U?Y+O3WN#WfurFk1msmx-f5m= ((((((((rmb8n…*:.v(A)=Mi- )x;H+۲yC}}h@S.Q92N'ip1ӃQKpHv镕Х*i;E 6]\EՃyQ[v=h:`XCbGfˌ8Z[th ̋b~l$uힵbѯ  ?Y""dU0O?@ۣ{a FprUdQG b8QEQ)QEQEQEQEQEQEPh0f]48Qf 8ʍ&B¿7?qDA8 z. qyV G_69ք e%Ʒs ̆%80b 'oʙo/k)#=W{ׄ_,DT<1-O HOJa + h¨8&.nNY9 H:rjoQtC}K=nyqU(%ԭ"]>VP[ Qyd :EPEP%ޡseŴ>Vy.H#'LVg]65o|灺hYm?i1!hݿ%_BKe봿1zCicT_Q6zu]$-ZMX920 ]V/1TPLZOZDsۭ2m pQ\Fx :ڵ(\¨HvRKC-jYY;wgz}&q c 8)Q6(yRI$FFBϢٰѾS [>xEΐI?8=OV2goi"KZ#${9}ik{zZtvp y HQ6Y"缒0N{c@5Mn H'$)Hn 96V;{(-aIZXbIKj%'&*HT,}WCwXKk$qiܒH*^3JmA̧̨ CHޮP{T6Ud<#yXs-~|઎O֒NָUgOv'LQHfF(6ڔKw\A2-7=1Vldy` [֜!fBoRqJqm1Ht}+v! oI# юk5}A{,4DeMЃ.u*q]Ϳ~mZa+B-ϓ7;vSѡN 'j-6iu5"#lq9.b'5 ov ?vK/`@ gM4(RNAr֩[f_ ]je0us.$62@o q)urqU>w4{s, 3\%$#O]ަ+MiBBc8|񃒽=`jdicnee*[:F܊@8QEQEQEQEQEQEQEQE>,(CK+ld*>^H]*MqǯJe^;,6j_v*A]yi;13Q:\NV>zB#+i ws-H%ؑM(2K'QYjwcO!@a'8 u 2w_MbQdR1SiX]aT2wVmm)MCd{ec-ťAl[,|QH=kVΊb{1"l(>z=+F(((6{ȜGn'VYI;;kJ/dh\:գ @e?Jo|+IO E^-'vЖ1ou\/Sh/OSlKɠУ·z} &·z} <Щ2hɠУ·z*L2h?:(} &·z} <Щ2hɠУ·z} &·z} <Bɣz?irh އo?i7΀"[S&Ѹx~tϴC=chz} ~Ѹx~tϴC=c'!B??:2}h/>У·z} '֍B>Щ7Ηp/:У·z*M??:MOU[o aZo! 4-ե6F gxEڕuąq-] (Am?S鎕']΅FcY_w8?XISk Y&$M%ޛh[}-"v;B(#3 :XVxˉ YNsP]--m-!YW(i֘+MK8m`G <^+qljQEQEQEQEQEQEQEQEZt-էFd 'ڋ<.T6AZ=! fB\$2~w+ qM yw>x؇I#J[4"ȪYqQRX-kh;Ygߏҟ&7n^XW vペ*F9b8HnVA-ΝFd{v4yb;b B{moo+*eE##I&s7DdRXssIH 7ڬFyh໳D'~U2rƟo}iu+oqyUΛKty.(ٹ6SqϾj=h5 71EŒb)1=hv(((((((4%x(4k1si4 jv)c\Dwms=魥^'EŻ?rQ?|h@hZ[4, {mJ.k{qʮMр@A棇N/^#-=*[ y F9Q ]yc7"C9+l:rK wWR?J6TۼQYTGsJ.=-L*{=J8YP9i`2WWk" Ũ23>OoNIk^ ( (3鱗9Y'FvW$YI.FIF=8:Ӡ? ֥e?Ƈ'vЖB׻KE-Ʃ /]эSQEQEQEQEQEQEQERR7hR],e# nm֙o>tH,sU5K['7WUomnWIPx B:M[ϴ?MC~W9sؤNO*B`uqZv3\iFbP8_Ƶ`Rk9H9 pxǭGyAn")L dր)2?1L1+8t)dNoC9q"83}+BM}=堐>A*f&]O ^Jo*fۗ˞9~Az%6b,0>v ( (0uiFb :Sɐz0+zMxbJ$0:պ+-jV[AZLhrZ/o h?-{P/jP_=%PQKE%PQKE%PQKE%PREP;tI6( cMQUG4 cs}GQh;#خ???GV4s@s}GUHicI!mg(rImh8֫g?aJh;#s}GQG9 1+?*nK?*yc&ycK&ं@ 8TvWER̿?/4wx-s*Hy{O py.PЮŠ@s5WVyZXᝢX13<[⌏4c4G4hh#30,c=9SGdЭَPBe^3Y,t˙fy\ v5QUF6{dh!+BbR|[(#Uv+Q]2N7 y]2ѩa@$P(((((((ώVwU1> Bśq:Lru= .QaIr!r1Un&H&(x%FJ<~timq]#bU1냞럱}F%F򕥍mf+qǩ (((((((((4Q@J-;O0E`r:b :wP|9n=9'`#cSv 225 331 C   (1#%(:3=<9387@H\N@DWE78PmQW_bghg>Mqypdx\egcC//cB8Bcccccccccccccccccccccccccccccccccccccccccccccccccc!" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?l,mOf$„NR ]¡FKbmi~(m> F^]ñ>oߥ 7>/T7Qe~υ_?Uy0Ѱ=" OJڛs݌+(ӵqǥDOu3vsҡ6ʶc(WjЕIAC'O“m6/2e? N"CoK(uCdJWrOO̷}J^փ=3"wͰn)/\y@qS$VDh z])m:Xu *ū(ط7Yu*8gep>i[pބ^m? O6\UtYH?JcI4F79`A^}Öb:u <6[*s ϮzbwTs˅:sߡyC*7T{Yw/Cgη}Go@??«nGq^^} <ߡWuWcb K ej1^"_AROcf KpBdJfsb!V_64gYϤ?KrҪ!;igYϤ?_YΚ:#3w_*OS:}!hβH`}f]ʵ?/.v2u<ʵ/.ԻFO+R{֏uC|e>4+lxm⌳k|y Ǩj.TNF{βH?eXFg~֕bQfIK:}!hβH^X2͵)SIA}h=ljβH?b_dfGfAR5$Ϳe>4gYϤ?>~Tf,:&Y=JCbMڵ?/_/|/d_M?ϼlgYϤ?_IO)1w>~T}}g:}!hβH>rc}}>w>~Ue>4gYϤ?/v1~y>~T}}k:}!hβH>v#ҡFIy`U//P]ȰƱrBg[<4'#袊HסIZ5eLCh(((^vN *VI.V)K?=?O#i$VQvJKf?10~$Y'CG!A`w pX--33:F0&/}cڡڇ `Ÿz{Bh#g%:E1FUrw&=bB9+F9V"fhx~^([}B\xq{J$G@FCk)mM=Mh.%lYtYǞ#tLh][HȜ3+j6C-VDg5- FTDh*:5,VRJJgHɸeq4RxdTgO]J gH#,JͶg2Ba'w=y/lR6ңyAqϿ|PRH Z'y9zTQEQEQEUz׫54(,VgYu?$(((*Үb`, r}U mQ$F#)C)4rwaLgbǮ*_]G0lya[iǹ*0zg޳MlbLG_zP5 xYd<cq,s i#Y`]W=2q}VF14lM;(R` 8O^3hCn hxI2 |)#$z Yvc@ꅚ9 Nqִ寛ͷ}x <\e@EĎNϪ?C8\1|P ]Q;Kl#䕍y01ާ]RÑ))rJ0GR>aTͨu(e@.I +j=p8_˷tEf.x~ ҇P$ew0t(@ވ icѤ"<_)`;qҪgOwv=y8qGmu oJHH # }3C<*-0G, Tb@9@x<1YP0EF`%Lg쿝#fs$(((((7?jQWk@iEPYu?$βסIZ4QEQEQEE4 UPdChko([ϥKhko) P\ll9rUDctO5k4&(b ( ( (~R0o7Go%xM|,+ @QL@k+/P(((*~zZTnQE]t?*[˙۲d$]3J+y8f7]1@ԋZySRH"Y4ykؚ]QGe%r81[LP-¡0$ܙ pG<M!j4; o9g?ҦP 4 ų?#Q>$C/ n iNO9Wh!;pힼ`_F.YIG֨C?7Va64 SUgjI;iOFIC%&40 hƳ44l~a#jrCMhiBD%RFM.dvoi+i>{Zt@*70_Ro,l#Ԕ㎇JxkL6{3]K$~[cs;W@xhWݬ%QT@V=3B[Lx,=8kb D[+wޡczހ$ӯKq1-3ec)w2"EmC PǦM pnPi.d˱C( W*1 MbNOZ(((׫5F?__yQ@eLCk:ˮ^%_.`b:g)霟ɣ&E7&I|u2Hvȁ{RbOѿ?Hwحi~m;7neYYLUu)+m@0G_ط9O>AuxUq5={KU~a_I+>OȠF591x˃!z5a#FUyY'+*+{C21lI|zT ipۉ^X=G@hg.҇Z7+ki%(12g0?3UDxɘF2HbOO@ǩ bY>jsIN'٨1Bֱ\\x$~v#$bfDz{%/+2x:Sh)(I%&<<0т9-D* 0jp9Gl̙6dn<֥`CA#n(B NQ8 b<7h,Ā=-!%)$q E0}l |w7ұͼ}mW؀nRA,qޥ!q!0d('AjĢ#" E-([Cȅת+0E2^# DJ2Pio fYKFs7n#/*1%blsOinN!/\s o(Ҫ*L֪܉R;2@7#ry֣ȟjEEoFۈJtљ"*3sO)'?֣r?W( gj<֫PhBTOQ\׫5F?__yQ@g$ Y]t?+FjtQ)hڿm_Z(6EWMF襢jtQ)hڿm_Z(6EWMF襢jtQ)hڿm_Z(6EWMF襢jtQ)hڿm_Zdq@ڿm_ FI(ڿO4&W }_BZJ#j~zZJ(="ˮ^%hu]3JѠ(((((((((((((8X`k5IU"R\pryC6wU6֭go<;K0 Ɖm!(` WӌqҀ+ 4&(KdE5u+&*s'&Zg|gnY2!p:?hg3As^֤9FsQE]t?+F sZl9]c TAj7 'P*7 'QIjI}?wZ?wox4@ox4G7 'P*7 'QIjI}?wZ?wox4@ox4G7 'P*7 'QIjI}?wZ?wox4@ox4G7 'P*7 'QIj=AU| >i?€%5~Qki?߱}}c*/ 141 246 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?\Il#EUT)>U[; ]#ƹ~'`=+6IjGʔGs">(3GԦBŕc8oJ滖[[olkJ .BX9Uns` ~运sN65Phbۜek\Z..o 7Eyn59lֶSkMyw~f]R?Oey Χ,<-ϰ~0M.m.>^o 7BNk:D}Vs%İL]XCmP~kboYm-MU"M&9ƛvw2\N=? N|?}K idhՄPpkԯ"TRy#0< c=o +Ěm]>9gg[Ҭj9UmnN;u94duf(>'^zz/)MiC/` u\4U=p\G:[WFj>׬uGk?P$Q >\,{F运x_Wk#֥gu4PjZmݴB{Bѫ+^iZkynxۂIⱳz/(=Kb运x_U/3ޏ3ދǢB!Tz#BgĚ<?>O0֟I|x?4BgGZgy'GI|3<<<?>OPsKk!aOP04vTkv#zTz[&OxQ`~[Q;Vxgzx-|O|| K 4ҩ"˜}mV[H,'K{Wp/@qɧd.gs+>? rI_Xo!}GQwKr|$+̺~fD)%`wϧJ|9}m?cOo9\Ï亲q$6rDJfУ8z¹GVލ>x ]1nVTry撊m.FZ+>? rQxŊy}Wf4Rv(ZZOkMFM2_&DhY$/<ɨ~f w~Tɱ*˜bx+96Z[b,3p k^å|-f4,QEc\mȷ e-͊ZZ2jLrB̲w<ɫjwf٪HU*aʋ E1^/'uVūGdQa.݇p=o4F? Ҷ-r#H(8.xU-UGoEy`҉k9<(z+,+Y=ޜ40-i 8 HkdҤ'Qd4#b&O1v..}{bCĸ4nm:m4jGtw#Le'OZ;&(?2ZAz%9fn5Ur]Zݲ!FU9i*-u0|Yߢ^M4$K=ȑԪ-4+.V.|pP,pSZE ƲQyv&+xD,+Z$399uxCJmFcTB\2y>|}ƴ?Qe&_*9aW$ y~x{庴 @8# FhvCF+.G^),Zu|&#qE^"}#vq³߽"3K&Xz8({7֣j7mM*[RxbW=k/!mhg!]퓏5x#H,jG寫gh4h寫gh@iXW?&o=_4yC@CG=_4Qo=_4yc@ycKW;bWPF)PW;S|٥?(((((j3zii/P,K<ӳHP Nn:?Zy!Y|;(0QJ?xC]>]9u =(Oqv=6_Gm!Ƽ^#{iVe[5 1TǭCxX5=6JѽW$Mcr4ֿׯ _=G6_Gm!Ƽ!œC/";6L'X4~#2ʐ bCLۇ9QJ\#֍̱VT7M=@1U#HUԷPF=WxCY~"x^PoLFHbmm1kVBzmY]}En/|#$kl{t58 ?tƠ8' 5y|w5xW6(M{~?ZK{oCٶߺxOWfY`%@ Dg%1mglf1LY/t4i}-gnc=kg4f1aСI\r ji lXՙJI#=4Ft,a $)#8ë\ϦJQf3Fh[Yˤ]e9}C_2y{[4?i7g:y֣[><{he`eDIeTNb fotoxx-15.11.1/data/images/dots.jpg0000644000175000017500000007371612616075370015525 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-15.11.1/data/images/index.jpg0000664000175000017500000006715712616075370015667 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:tonemap|resize| 5http://ns.adobe.com/xap/1.0/ 378 400 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((Rh" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?VR8QW8ɨ'լ}(ZvN,yuHJ +y-{Lj&Zྰ&\<{*H#,#+أ^}Ny=ŕ"$czv&8Vg\scҍ_k4aü=GMVb6. pfG%QA#Vڞ[xx3 >8;“&V\>eZ=nWd+0z/+ҵM]#ԿbD0] [>g=hXu!F运05^ 0`]GjxYvw"mxX}*P_Wg`k&7m1 ˵֚宮>S3lrOGqyR&H܄S运׉o|9FIYo/u;?tLCq/"m zqc<运 s Mf$({Ehy\ `_QEQ==XE`_U3ޏ3ދ`_QEQ==XE`_U3ޏ3ދ`_QEQ==XE`_U3ޏ3ދ`_QEQ==XE`_U3ޏ3ދ`_QEQ==XE`_U3ޏ3ދK:qh'I0`u/Y]4r[+@C<"D9Q+:#QZjq5(;v9Nѭ 6fn^FƦp_Cڷ^+xѝQFKhXm(Yd=jdg:mZbE}:z Vf Z6v<"gu/Urnb+9`f~9KZѐF,=w.}2zzԦ(asJ./gW6s>=W^>mqo461yv`! Z>}( nwaA;m$(1ipZrn#hASyL-& bKyBT)ArjGl/- Ŭl gsIqiW7zj0 ǩt^u>D/QA<"sút&хղZMB 폭ExWڝyFH rs;gA<"( |Oqƕ Hqpʘ8';Nq5е=[JNC+ȳНZm`Fu-ܡBQfFecً]N=eYSrpA<"D/Q2|G+[ȃxE| DVHkK8E_[8KsU}b)nT=e#¡bU`Wabl=1S[k_kBj;‰4f4,pĒm ZaeKoVSN}Tm_M:ɲD9+9i9 ٵOjͯua_l08ֺmF[/]^ihp'};U{WӴH^[\3"y(TH.pNH5i/#YcDUTԁ~t[z)mUԛңCpӨaބwkϩ~թIm#EYYp ڣsds5=O-ڙPfHrShbY++"nyP+}=ڦuI3|XL(/n4Y-'Eabqa2۸ӞdٴnHw.p{Ls–orfʳ" Nhu;EZrKbo k~B*;f̆zsjqK߲FZ`sG9TR]v]WjCwd β&'sjI%wk_>@XvJ\\hY.|[z>c`ogSBśg`sWVwgisd00N:{3Zrz@@M׈5h-?2Z7ˏ VĽ7g;bkI|[ʉJH]8!}^-ͼ::Ȅ;(㓟R!hnMͯTl2]znGKxizP}3\[{t17cژ5zRoHP_3˼ E1e=s]z.A`d6fLd>֋yK5bfu n)߫&$<ټKͣX]%qa0 /#J־5;KOSq#-,+$>yhpCuij<jZ^7ojS>]Ѯ6䭵ei ͸ѢmבN }kml|=B4Or ;; o=L쎅k) fdͭpXxn8Qϸ]rX3 _][ְlbSxH2Н9 ǁ1]Ɨ6q{ Mrąǯ5SSK N͒]m`<$|a+~41KOhbiKwypy]k?^Iu6Iެ8#ڰɎOT6@j`==zRkZeז,P(WVh'D~{d1.܄ 8ɩ5i- Gdew*<gZ*g3}{j&Űh16zgi]KӪHhP0kFnEii < ĥZoea%e,M2n H2JYqoʁ2) J( ( ( ( ( ( (uNHK[kFD+Ǹ.EZƱ F?ZznWQ^k4Űz4=G5izmt{&+0KIy9o^]]iREGl7Hx6S?uJK^[][{JU)[I== G&HDWo01`s6 2? ^ >kyX <[Aw4t=w^@{L\F*P t*lsCҺ4?Ky/ i>vc>տE DΠ5d ֫, `fD`.wԖIp/ y<0N@lq]_cFwkWE r[ asܙ`j+G5v\EԴޠKͻv:& j:-ukAДGצMFtmR{B{ڜwbѥBdb)܂s{WmEcƫfurm`xKY2=?#Qj>žӚZE &x[@0 (h/`5y: +4-kOV7FfX,άq@鞕83įjyQ>q mB`WӠ{[i" #IǢyz5Q[K_ևyι l w}sP&c=ݕ 7;_3-GSıGH6JerF3^֭keҾ<أ;H2)&:CI[cϴo ä8 MT׆ZOM_[iKi~[osnH 64Xdj (c7AӬ45jn7dק9*Z iXJ( ( ( ( ( ( ([q_u6X u.pJs[35ۻx;ՂYKTXg'p9zZ7.u$"[J$@vp29[a#GqpDmP b@ÖPFH$ <iC)>!#+4r^Tt@\1ƭi4%Z|2]yfV_%VG+vYO "Mpm ngs •=jA#^a  cqU\^_;wcu)aȍ$+@ꭨYjq['(!8/4gK *ɻv[=ZҬ좍3iKfRy&sv5i &K"[{M 3Hޡ6>m kF;nvXlEM*?3)rpUcmɴ#3E p~fCvG@!q5sp/B V5-N54(ngh0p±$lQwO{#k[Y&PX;={MQcNd+H'O^s\QJ)qF((b\QJ)h(ERRvcisHPxžUKZv1ՕNo >J#-@zsnGU&EswaeikK^,ۑkڿt±a sIEv?'P]i?KeGx;W;+4grdWjʒjzގ'P[^9%YM;O [|?i-놻qki\]w!Y=pbjz,S^1M E6o-E Ĺ7,襚 5P2I`t7ۥe.+ӵ;5>Ȧ^ZKq#73x(9?EkYOzi9c9`:Mmpڢ߸% ` <]F]vE,jWNi[ji!rd|j/$Z^jn$Y8ߔ'$еyZZKO/QkO o7\Uv|?Oz!Uʹ$c*=ln+ +=2ys֟n~Cŷv[xQ,'7'=0xj*$+QkO o7Gϵj)j?i-uEdZZKV>֟n+QkO o7ZPOϵ[ֵj?i-uEdZZK QBFؾ\pOujXz@!EKGY_M(ֳm}#8s8j}"u=6 ]ʓƲOt-լsi#zZ:_Y{aFE0O!NmA#1!70_/Sy?#MwJy!MFn D1>jPFg#SC:aΪY,w ζ;X)?PG']$cME721ڧO~lU7a(Ls׃VtJC:Ui-7Mo9Xx3VK7) pe?qJ^@uR:.]ZD@)g9[H|9irMmW\!9by8rW^խծ$ingulF;4+GuWX<4i""IBpTh=]sJ[X-VK2BBUlqyķ-Fws2OkO 6g(^ ÌdH[~X{)fV6:,dnLZJsWs.drx"\MX`kKӈ@@?C.Z1$ aM+ U8,`斿 <|nkKi.qZެYj qǭ$<6ۡ/okБ h`c?JsHlehA$%B8uN8=*-<_=ܤ{T 9m%`8IC\~`|;2?ZԟS?5FgaNյٷWE4W(r99=)tǿI|뗸B0H=BޘJ yXi䲍zlj8]mگ\xQ+Giq{-cO\+bO >HZ+Frx-o6]\"Y@y ӥ]Ho%4 :\.qgW4Yh<;+?6ScxTpieƛ 5.dȾPPW*iduZ8Q6"HI85MERQEQEQEQEQEQEQEQEVv?֍gk,?i!E!_JAKNY沱 ZŁLv~"m}?5 [?x08`zjS}zE;b]"g=HTӠ9R)r W Gc~Q=nʈ*'ڼrj]}GQ 'Pvoƽ>hh^)Wtn OĚvZbmHUp>cZ:rzݴZ3xC3 C3rw+_XMu.w*.7X{׫>^'7#f MZ,Bm_OMrsF=sJ8; j}j=kQJnk \" 3jmtgZ̍Sf$;kYk"3$H66=7Qק uvY k ~-.ZVk]+%_$K&يX1 t=jxF{day 덹x<{R 3Ezl4t #BISb!d}KmEvy5jF]]nCIps!zw=&[u9-d]Qak I!^Uv|yJE#1kWR5]wS_4h[khڿREuS:]\j6QIs<\ T4N$ZHUg/#1u4޿/8|DڂG QI@(@9tg}]!\yAQL@f|F1zK_{OPԭu[LkO=mRFIǹ5ōoy]Y0RY!$g(_AoFKR 68]d #wzun5 qf)`+w'9CmcL[-QS3rGү`RJޣy5':o.5;(Cs}hHĘd`6u^{t kFG{Cʯg\ҡlI6/uzo )g\DGxNzkhOw.k.=ԈC 3ӥ5Or̆yeQKQq *WK/bUb(]p^"&!.4 [[[8W20~n' Z{aMd0H#К>%ki51||(Һ$1irI^L nֳdZTU8═h7L֩jpyK퉹UBHVPP zR}\A{2*=syUfjsW|mojiVF$3Co#̮|A>DuӬ\qgȣ K2y\[}t(k>\;u zc1]+I֬=䲂H4 XobQiY&f%A] }sZ_XŮ;ԫ d#oNL<9/ w~f|_pܸ~j ]]bm\9޶$`;傑ڋťTҍ20).h{d&kK/t% #s`7~-4ӮE?(?z/jw5P]8̙ݿq[jze&$ nYR 9Ȫx̐@ ngHc ~4>_`]{WbkXH-wQxY *c5_o6*|Eܒ*yg]>=ڣ]@" I6A+uDZ)-s]$iP| zѠubƥbR1sP,tQ́ЌTz5i⫗ʻGI~]F鹷7mYխRZ@oIXc V8@svBnsMyy^ o+Ooym2]I8즮&'#`yؤ{f`ʠ8m~R1Ԗ9[%oI|ɢ8GmH 9MkmbC Ap^${3R|bj-t*j>.u;ZN$?t}]s~&:Mx)'G." =6:ƹƦeapRF} ? s) ((((((((+;XYa_MFހAR/i _J5="R|jFb1T6cl4l3ڹscb7&l`y?Y>s/9+#ʰ8IJFpq}|?ai=2}C,rۂHOWFK1@'yvFQձ.Bз.'e<,W7Ye``אFӴvSr,܅pz k/l5l㸻"CWh퓷'>5ۘl`Gs9tE{j: rXI#TXh4t6)<6 g9>XkwzZٺY_* 0|$)|;ifre[ʤ?*pFG慵RՎǨL-h$1T3ggiZ)m-{2,P7\UjGT/F.-Kcc,伄ɭ-+7>ך46[y))N3ϥOqKvVBũlsn%x9qU}yKkǖD8`{/ú k%ݧؓ۷ԜIvNvEi]Ir@ʟ;u:ij7|՛A0ktPaJI;K zUENvKS Y:ᛏkE-xfxHL0AqްGl;"p`pz!ΦVXy/nX#y ,n6QNzsQ~M]t eU*n~\zW3kIa%aq)G^p>@P1=K(h֚[KڱEW$qסxj+ϳ-ΡI >Y09B*]#F6K<ӗ.Y&AHڊ*=ojp͡6EbIF|o*K)9ƕOW* [~O2v?oO54І[y&EW $mڠddx&w!fWMy-II5]R$y`{ycbCU19dqZl4|Ff3/OifسovN(ziZZxZ9mȊ8Du8awO8#Gϧj73Yj1~`03W?kwwūKe  ?7˅#:&$=="E%;$d2|SNVN*FQEQEQEQEQEQEQEQEYo@hH`)tҐR/i.ZiZPN4ʧ=HRӡWX&z$g=/;h\>nnE^@P{5式FS8M#9SPǽ4&^$Q7R3ֹG_8HK/JOt2Ubڣ$tjj7ܤaDyEn'I_c[/l~텕Mހfմ_ؤݴr7>`71XAywȪkcX]`[NT&7.?yQzDJ%[#/,yv9:eޕJ}H%0E1kc|W9xwTu.\@Jd$?'9b(zOe4:*C-tp%BQs{ĺ$ܽ))o,uV ҧ]BMZ+8~,/4 .sr߄'\ZZn8 O>ا^ch˦o[-8Y*Μ r@E _\k\Z2w uDI!X*6 8'JK{ bBb|ɻsP5(>/˸bb` PU.kJʏ $q֭R܆ Svsm>|>43΃NaDnFv}5ضJqN\opK,.m Ŵy' t*SHży! u# \T"SGzM$b-j=7K}ONQ3nAK!~x45 5՜rE6UM֗\G`&[?w''R? j62iK$(aYT1)\1ڷ?k-淫2oZp[> L Z/tMSLO6XHbb>nxzRj:DvQ\Iy`G#J|3 . i|nDxSgnxc"7usemz5dy'>SI] NyIk/{kyE299ǥlPFxJLZfmc&rW˓zQWUG@QEQEQEQEQEQEQEQEQEYo@hH`)tҐR/if^:Zj#;ʞNzzV-Mڏ;A 3$-[Fէ{$[1q[2qjmNIaYU  @SB. ǩͧ(-孜l *@k+[-Vb#7@q5mkuݦq,ʖ;<yUě~T-W>0RzeܗFVLضq{/[U:uܶ;L Y jxFix/o.Lɸ @ޘe#i;dcU_IhkI`kbׄ(qijP,QcΊY]JraD.Mi-vѴh~RλK7+^,koo0ib&|告I#${UOC=-"=R{5TZ5;w2NsϭYIj֩-GVP[4TQ(P$pA֞KK:{}_REň x\L[vN~/FmΥ DW[o -U ivϦ)RFP2 H#k͇ۯΛ0}d[vT8¡gK>UU^}gPxm5^ 6(bX .r;z.ٷB4mnY`9aƶ5v3Nry%W~]|lz1qYm84no&r]Y%2qkȗ\X"Ÿ9LIh+ b88okm`[$hE qmf5w(@Tp#9lz7i;k9CB򪲪ao@'cA{ N$aU1JyHm)2nn.{7<")$IaQxVϱƣ|[ydCv#n/p9봁V %u+k떙 aeP?Ѽat[׌Baeo7 1OZxM*.tF=*B<9A,~MJeidX$d!Lq?xI kD]]@%|tlr0h8go%َ++Ym{UiJq3Y|Lrl H۝˜Ho-o1%; {$HuI;h^݄?W`N1ʌnC1U}m)5;inn ky@#C]l\@f W#~n5;/2[vTky߲K{dknF ] c')QEQEQEQEQEQEQEVv?֍gk,?i!E!_JAKESQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEVv?֍gk,?i!E!_JAKESQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEVv?֍gj,?i!E! đ,nW&ȿy~ُ]Gُ]RyXGُ]Gُ]RyXKzK(/ƏƧ8~>~~񩨠~>~~񩨠~>~~񩨠~>~~񩨠~>~~񩨠~>~~񩨠~>~~񩨠~>~~񩨠~>~$!H&Y IE-Pda,,Kqứ9ְ᷺ ]GMF SFƟgp뇾.ofldO4X`8NN3l/3k54 n\Gf _Q ]'F[XPHcpRp1 hn"id69|%ҵ,gGo#9 "?/}Soa7Ҭq*$o`IaǧJ[m>_\Eo8/+F;HH7Co-e[;s;FH@F ;eWVqe>%vF>W`Fltۋ]kK d*+,7>`S !Hy+ l1KW7ZS :VY J FO˜d`٨> е#RHybI*]Q&:\w{s wrw;Nr~ZLViAMyGo[Xd,UwQ]3UҝVa]+}{mސCOˋzGi?3E~5eEy_ǧjj@m@ LIҮ +"UH}b2'/; -Ww#W:[#gh1⸫Mw6w$JHQ8 84uѥmȶ/ x*ʒ$v B$zK RW Bğk6Aܒzu,-v(((((((()P%..#lKy#9ArAY ާw4/"VRHUT`< w p>s@MWo+RKk,kt  gnsJkȩ 4g ȧ LJW-'``R-s-pye~@#bj?-a=sd/>s@\d2\Mss${'8I|wƩs\70ۋƕJG$c UU#-M.OcǨx~[[$EK -15. s'g>P  cmKaͫf+{iԯ^z}7\_[hw7s[[ԏ΢/ d'',4ז[BS' ݎ:A|Yͥiefh%.[֚yL' s! TaEsoN}&öO:\JieT=3QYj8mn'i]o=u%Ὠ7Zmsrn {'iz)[X[ hpeU 4ktIfo9x nDmpA*}Q/[{,h8ٔ0S AMѻFO/M%4dr_@z_?Khɳƀfg6ܗ?qF)6ܗ?g7bg6ܗ?qF)6ܗ?g7bg6ܗ?qF)6ܗ?g7bg6ܗ?qIM%56VX&ZFnE} JhVOe4º\ & v y[Xs;K&t)'?qA"ԴwnKtu&ʷeo70f+l ?O2_cԴiYiDecN4;ۛɗZoo` <$]G/G/Eң}F{oK2ͨCzV'@Hm5$ZMrVf6yWרZ<?<?W _MgawM @Ѱ "dWn_6PJ4ՖPd=y̗̗;-iu;2݋vh;BFbO-ҮQjZlkZl m剞ʼSn]>udd;ki4ki5/G/Hdo,o,AK}QK}Po,o,AK}QK}Po,o,AK}QK}Po,o,AK}QK}Po,o,AK}QK}Po,o,AK}QK}Po,o,AK}QK}Po,o,AK}QK}Po,V/-o-+[ymH ;̗c)rf\њJ(sFi(4f\њJ(sFi(4f\њJ(sFi(T/;y!i v50?=Qr@ MڏVݴKHX#~8Gsqx ?R{4̂+&y"̒Ĥ<~5j?_J8a:|[fɸK41O0I$sGTc0zVw.&Y*I3GDKL~ڰ|[ t]^{Hm$e؃ZC7wj?OFG*^1{Yk{HB)*kKeTe(/di3hfU(b8ds☮Zݨ9? '.Qgkpa+FӸ+o:-RIq~oKLj p0{v4Ozyx(e!R5xmSW?g hm#VxIcS_$ߨ4<64 `3g֛Qr@ IjnGk+DֵmZ JaLK I>G(ݨ9? RPpn0?9T $RlEqќܛAj#̦P qA91L.Xݨ9? 51<,) rҫ̢m|e~$IZL*O jm-LmpNTETض`ǩQHe( ( ( ( ( ( ( ( ( ( ( (U{2SP1v wb=Ŵm |= O @@A5CZ)#8h>4=V0EZ 8%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@-PEPEPEPEPEPEPEPEPEPQEfotoxx-15.11.1/data/images/mosaic.jpg0000644000175000017500000002634512616075370016023 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize| Fotoxx:tonemap| 4http://ns.adobe.com/xap/1.0/ 159 349 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?KhL ,9/ zS- I)- 5BK܈dqްS7B-tMEA@c^Jeg1¸;P3G!` cWt-ɍleYѫجF5fHRHcc^J6ZڶQz),$[co1Uo.<-pmNI ^ ٭Yߥ >k>ߵ j vku$ChO4q I'S >*XKw0.7c4AcX-8/٭?¸]Mv[}Cu\ioPr{U꺝PW}(Vakwwm=}6[ڒ@$uY֭j/lFK#T.u<ݷS}iIG}aC(5~W5HjZkܥC_H職} sP\i4{%neHP1^űZ.wew7m\TfX VC ΂dF>r ó]6ftH/Oǚb6>k>ߡ}}C)@=C~Q{_{_MFzw?—ֿ_o?ֿ_ֿ_o?ֿ_ֿ_o?ֿ_LFz?G™ލϬ#Z2mTbލ og) WFo7E+ [B3,U]COIG^mUMN¥ڟ_ʓب;3Ӵ++ _H PXrñ+vCIr cA$ў0~GY$hq s>7Jv nK+3b=ZHb7VH–UM@kwsFb1G+̈#€LAH(6\zZ t}o].o&@`5NL~RϮ1FH#DW1Z7R='aD?]m$+BwI[C!XDr}V>/.B*Oi-sP`LEfGZ{~4"_m2l k%{S31C5xѼ~&8rXNnMo∑f%1(v3 =5 ӎdW&fЮ7tJ<"/o[ 0!ϔ'N)9HaZϷv+$AAVnǧinF_ݴc8UE yk,zteQbPwumftQswkS\)x233Fy$ҡifid}VkeЧdD">¬9>M4tos3n/nXXq")IG+)j:ƙWn|:@?Qv:`+kUwP.@E_ tK@f+;kv/1,Ӂ@q* M6ٮonb TӚ" %)dy!0s4ZK%^<6/4` <瞜q[DX>*4/_7\HSaA:ץ_6? R̨Ck_DNK_.cJlgqҵSYzޛ;̪33'^?Z; RmFX.V9cs'H3SZu}ԒR@E)b\&;荧[ȶPciaS5w,J3M4-[ݙfx bp qUtOYZZI3|mH/]5h͚.ŝ$V]c5aCbY4I-p!o/=9 :7!E$-ᰧ7~k}!ȑ)Hq'Y>qi^h!",xKGl*dH|W뛈ZY\$hYzּG*l5t??WNWVK;u`rAPp~U WbUrI1RR"A@'ZG$"WOyfmqyZTRdЂ++OHF(Zoy ;nϯnlp v"G )oPQ̳eKr}zv?`R>GMCf~,n-vZ;8@DMk5B_C͝Y.BvC;ϵl- 3BdA pXq֝r'hA( {|7%)H\U{`H3HdVn-7IrX$e76: w^(,E Ke78p5ddB23ɗֳljtEәgv ʎè F $;Ŷ1eVP=qE4.oi$E|& {Ҳ yA;?<$+iT[]J}=%B(V81N/[If"s֝s<EzJO" c7&oo>myB&>Z,!ӹ'R'a5a.yIj5Po-m5mi%8j)-9.S3XZɋ\\`.2Gn_IͿmoMحlA_li,]}-Dx9?KPE$^ " |EGT%&c )(fϧ{L'&kyJǧ:YhQ/g_(a燵},Qj 2V9ۼڱeMq=qq9526“:${0OR$&3м+i* 4'TzКΣ)9*,?G?ҥ)I<~,.&(QEQEQEQEQEQEQEQEQE(1?ATpk\,5Ym?tQXZO{I%ϣ]N<>EER^GkEqkH!{ւ &rg&|]yu -HK {V;X2)IttW{^^H Z, q2Ɨ7-Lo7EW% gݸ8i (rzo.<;)bB# mGb嶰TFen :Ť}#\|AX֭j^/k>㼔.#K<cΞ8p)s+{ hʎr.Q =Aj)"JdSNE:]mIbGb 9Wh$iQYگ>H ?_}EfjG#(GPU? @TVoϴ?Wh$iQYگ>H ?_}EfjG#(GPU? @TVoϴ?Wh$iQYگ>H ?_}EfjG#(GPU? @TVoϴ?¦7r$s'"5 OE#7䱶Դh/!,K2HRi}ܾeŨ,շUަ;|TIT6 h-MZ(jC0G@py\՝CJ`H/xq"#SV~TSEt &x#H'?{cܥi+ |}hQ@4snY6И"3yTBAvĊ@0*afk9'cOʏ?*/wp"X< i?~T|PƙGꟕ?{xѽi?~T|PƙGꟕ?{xѽi?~T|PƙGꟕ?{xѽi?~T|PƙGꟕ?{xѽi?~T|PƙGꟕ?{xѽi?~T|PƙGꟕ?{xѹV|Q@!?s?*Ftֿr_T yA\A5RQEQEQEQEQEQEQEQEQEQEQETr)Sm ?Pc/>)>7s^O+}=5:ݬf1pb2r*Z}?N5\1%#w4-7}k>6.!Yl3 vRG֥|my p$Gt#+`}OKǾA*9ے 슁1(AL[KOQ'7_'t8e_J|MaEo43L TLjn|?m6Cn?<}cW&[?f?|%rw2{m, 7n! я%6qoRL0v3zͿ?|%2D?1T2qmgCr,v֨=Iop}mj,w]"ҢFGزD4\*Yg[v,'}5NY%6 >d_6s̶o4 ,2tc~lߟ ˦hit+mf'lpFNF~.,TH≲blߟ>/G?c#pγ?AUMBo^gDsqE#?fotoxx-15.11.1/data/images/brightness-ramp.jpg0000644000175000017500000003116212616075370017646 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot0230#0100Fotoxx:paint_clone|resize|tonemap| 4http://ns.adobe.com/xap/1.0/ 357 269 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?KhZhY$Pĸ0oO@"қndW3[#7 ]ǧw*Κ}ntqͧEJO@ѯ56]EEap:~tghPP1һAnYQZfUh<>4jKܼNrRˍ1x-wQ=krR?]*Ǔib mR'5\AL3"k_Vu2]Co!TȯʏaWRBQI@Ik_Qk_W#x>y@-/<ɕʫ nQbk:o~Bϐ+@5~(5~+Z/FI4)$Ѫa{c4]gWk6Zogk"-8)}akm4^ 3 y=oL;z\rj_@q r:}ޟ;ē<].4M0s}z}}akN̷dUadW+e꺶@YrʑGBej0iV. ]3U[^F9{lW1[H41Ec8M x==|;pڃt-U$>jY/ԧޤ,de{r8k_Qk_Wjo:ç6BJFrqn\ZEl-~TRk( ܞ٭A_NWjam";rCus&`xqyCB6_ʴX7Z(M~)|OS(]kgh?GK?YZKg\g:2;t-՛tM'@-Yڎm IIjZ8 E?^T#i|N J̨;柠ZYL%wAedV*N{OȣOȥj J's92Iz֤՛zH]y(y*Ef/ݩhZ5EZ5E !y##fPJP{S͕ѭ-dm JCS&k|<k|,3 4DyJ684ږDm2FnZZ5EZ5E ViYonYa =ȩ綂W^X"k|<k|,3s лgڨjTwobRĤ0! O|-?"-?"=q#d("ԍmd, in!bRO-?"-?"chfXħv:gqD\:JYV+$qZ~Z5EZ5E M mO)wԱ{ˌ.~_Q_Q`SZpz_Q_Q`+I'Yy(y([y(y(֨kOFF<k|ɄQ{Mh4<$jY,vZf+# sޓs F2l@yxʃ~"yv}O̼/q,oƏHz@/Fh<^}ԔPgѿ>m5%oƏIEGxoz7RQ@/Fh<^}ԔPgѿ>m5%oƏIEGx@ 5Jn?"bp-?xII%7:7FFw5<)mM6[y@ 26OKxo(m4}/FjJ(?gѿz7GoƤ#<^}W$@pd{kfM'杯yA\ Y(?!K?]CHzAEQEQEQEQEQEQEKT Iחe?oS裩5b In_dQ)f=Nji4~n u LӅNHf︜]W( ( ( ( ( 7zAUX&H??ʓksN<K,tO RZǔPb$=h ( ( ( ( ( (gXѤrb{@ot |Y x/^-! ΥdzhNMcx]PƧ#]ɞ7_ _¶((((((oǤ[5I5kP%BC')-RD1Pz@Q@Q@Q@Q@Q@eSod?!jVGFK(管j pݍBRQEQEQEQEQEV7l3*)v`$rԺuj[A[xqғks(?!K?]Cr_)gT?bZ(=h(((((}owt^~5W3O5cvywӞ%QEQF(IqI^hր4(ɼKDgS+Pj#j8m +Pn4+ho/5+0x UWF7zi`Lz$b6rnt{y2q= U \|r|C{usc֏V* 0X'H?T[r_*-R4hW*A-yA\ YD1uuuqFd1s^B !rM&h#X;z)Za|?lf&1']Kd ?)GގY֍ր1*yiݼI?¶wZ7ZKIUv$ }h}h/ 'Q F~=VC+gqq oIZjD ?³5]~5M.@OۦG5n>D=bzD^;V36Q%6¶IJɤd O[~SÌkwQ8t.?wd$~ZPG7DrGiZI(ßȊע2Ե[PB*|/Ɖw 6Cy'&(#8WdQkPK((OCfOC=5-RD%yA\ Y"C֊Z((((((_CCֽdj|5袊(((((+t?ʶkt?ʓksR<K,tO RZǔPb$=h ( ( ( ( ( ?D=kF!!^((((((OCfOC=5-RD%yA\ Y"C֊Z((((((_CCֽdkkZ wRE(((((+t?ʶkt?ʓksR<K,tO RZ(?!KqD1Pz@Q@Q@Q@Q@Q@dk?пSֽdk?пSEQEQEQEQEQE[5I5Ѿ3jVمo8T(tc{>֌nsp>r_*FP%ow)< (o o < (o o < (o v}ow|{W_κ)ow+7V𶑮Z\jp̶LZ2I?.3ʎG4K'7;%7;yM?@QQM?G7;%7;yM?@QQM?G7;%7;yM?@V7;aTȱqP {t,ݎ8>ksJ<KKQZǔi˟AY]N=ͻˎ=J4Q(((((((((((( MIm*<6 E84nѡ2RPw,z7Ry5Y^#jau4-?V{-6 X$(BL ?/xhiײfmї&y${(G׆ 9kn/Yk^ hdP{yq^c,Bi6/>ڹ (SDF+b2s+C1^_\[i|qK2IrR3f9q׸Vs|kyBmev`dGJ_ًiI$qZmDn:}b,3wMkx.!P"aI#֦C-I|yJG'MMgawqQIrUݠwI!G>x~URi dP}e+Z X^iYڑr~qP֗w,E8- ?QS>ͧz}fA=4!*цy朑eDHY- n)ue) NvG"W;yŞ KH.P8*G=mgw8DAጜo :T5]P*rcqiݞUHM:[ij ̥9on(Omrw%: 5},9ux>yQF_Ir4NB$?uO==96HB4JKQzʕeX:s@RbX,uCu6b[k$Dv}3ɮ-x:ʷB,H&R <昮jT3\yr攌NԞ56fR?2dXs;)>Kു8bNZI*ԞTO|5H& 2M+ !fot@Z\R[m~iT# vqs3v( r kĒ!FS?u"YpHSEfQ{oxg%ڣw 'Gڣw '@tI37BAvy0r@)>i?>uG@?2(\њ@U2Aޖ њP8Q; sEEII׍$ C/FS?jD,2I@ XP8rYtglO3Fh W&ZSԞ O/'Pk;k['ܴQqR<!XZ^SAdL\JX43au-=;0%3=#hV2Ҿzzm#go=m$cՋo -Zm9Smɓ@é&a}n܁.@J|WVi'C\CRCE軙0qǵlxGJ-,BJΠr򣸈,]i5ͮz ʲ&yb՛OC>sk-QRZy\N?*Cj !E+$ILm{˸#G~ޢ#GZɨf-. >]}W}ú徏qorv>cqF:)#?N(ūiOm'HduǮ)iQɩ$NUe H38'ã76&vb`v{S<se, KF9rܳ9ހ:E!*AVΞzm=kbqHv# z`) (++ O?VexA]jO`FyA\ 80WR.$'iRbO/ƌIOI?1'?@ E&$'hğ_-|y4 5TAy@*$'hğ_W#'|\kڭ}b'Fdvlm,z:[oa-2[xL`SOO&U%.,?B\Z5l}Vφ{Zcyl!LyUeYfC@8$z:2湅&A8ܪIjm!Yzuxlⶏi#;rJI?o?OI?)>'g yJsxRH#ƚ-ECi"8Sa_Kw$ٿh$7MrF9bɨۘB Ǿ)u_ju+ML8 r < @udtV>Zj_K{ɝdI!9k\ ?e4=5mT'."X!(%NxZ_I/?+IEs^??$0-4\V:J+x| %ocI/?^?.:J+x| %ocI/?^?.:J+x| %ocI/?^?.:o$c!NiA4G$0-4צK;G>?֋k{ K[{{(#]o2=/BidY ,:/eo?zw gH ٲ> }֣LҠdǒ"#?Aޱ[|,^u9K;D>֋ VFT5KiG$rێY?cs%?֕ƄZ?hbXF0y&FRAe ^[|,^ קp%,S$n/JToSQɥh2ibß-I_=qelXߟ->?E1ivER2;W }'\pqLkr)Y;wڼ)cU\0}7?fotoxx-15.11.1/data/images/match_color.jpg0000644000175000017500000003134612616075370017037 0ustar micomicoJFIFtExifMM*JR(1Zgnome-screenshot6Photoshop 3.08BIM resize sharp 4http://ns.adobe.com/xap/1.0/ 207 237 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?洭+ÓB /.Zf8$E<<Ǚx3s3ۿH֘H^6-r 'D[#'LW6#yRWoe:|m#/IGB?4K[{q8|`=AֺO+[hְhG-*J\qތV qg4'?IGB?4i^"`au-K2D5yf[#pMBQƴ&/IGB?4o].etYiOئ3 "Gkgp($cp~YZc $~{ğUiw$#:H@]z]kXq_*ڝYBT<|oO!~?IGB?4⫵̱ޛk<3:C},$21;c{L% 6 FU !SXW9 ~iQ?нO?f֍>^;i%FPc}O:> YhZG7̤ꛕUQd=N~{ğT}_/xO*Jh[Y9H 2rQjZ[ek9d)<%pXg4Y+?нO?>/IL~OW?нO?>/IMJ?/ҎP ~iQ?нO?oQ~r}_/xO* ~iS/ҏ.;~{ğT׶k(f@?:?/Һ}yf#J$CNN}1\ؚʄ9vF;2h#e=d#Cɠ g:U\Xvjl ʷ=GKKDť]V<(#kXԩ(M~iS/fBKLUh+.{xa` ){5 }*{-&hޤrxoP?WU*+3~ivDjJm>cW/hERxHq{S?䮢Flкs68< ړ5{wmr*Cg*u$N{X? B#!3v-s\KSAC$@o\RTc%ef0nԕ~yBZJTݮ ξz_5bJ]OOf7lV8Nyt=xָ% vjg~jti+2\;Jwb9@,|'s^l]Rg ?A(¿Wp]@|rWKg9#9=h #ˏb߿"|?p vu.۷s^ 틌t5bZ]%kz!Vh@t" ( #l7}w[G(TaP3.(,r𮴿 k?E𮴿 k?EutQvG) K Q K WWEadr𮴿 k?E𮴿 k?EutQvG) K Q K WWEadr𮴿 k?E,Vfd]NLiP ]UH*Q9)=xnu֛Nq VVVa!@5$ak6)RGqCGzWSEg*pQR$qx:\MxWolgrA<}9{x_ooN**Nφ_O+e]zi!r" F9'+?JQVRȠn[ =>M KxGqm,|R.;jv!aZe\_:HGE.88Exk3a|x֞}aIq׵6XO-aV;~qHf@kU?e/si-1fWvOPy{p"$,?1ׯ?C@l/6ٕ/57FGrѢM%0<wؐ}4mYeYUa7Ec?:ES~p<bjH" or^eީ8/T;{"1 ,m8m)W ,&1F_#ʻ۵}N(um@"ᐪnǷc4.fмfTy˺2<dPȜ,>0(( #֭]t #֭] _rVi=F[պ2 }z~I%uc}rͩђ=2f5Eu# Fw; ׊O\A"^NRINݙ*d~̟%2k p*h6Ư=@|19'jި#ϲd/NKxW`0 2t?0> .$Ѭ[M`qqh2Zܭ>Tc 8ҧh2O*D;5~cXl]ӏ|xzwZmkQŔh!\<8 yj!e8?֛mmw$C*GLAOOzŏ@x慤ۀ[}ci=ݞ6ws@ 3 .1Fp } OD8ϝ6o}~}P 0<(B-3)DG3E feUˀx8 oZ|m߳x޹ǥ4_ٔGp STdѤ{kwśBA#Pn:?*Gʰ(h2CPA1.0[g8#棖5l$]VPn LqN`>`py⩮>DlFb| '8[ T2FI受E`Y~dKKMWbKEqϯnkJ٭Ep򂾇Ͽ(Š(/ZwҼ/Zw"g|2}]Er _rWL":sJOqԅ@՝0Dn#G&7lr}~?:7[mNk>ߥ v 0=?J+m>ߥ DAB\m>X.U$C17qiԀ((޻n\ZZ(z n#!sQER+k5fxUx,Sc%n OE: (K*A #DRǰhR) `>ԴQEW@VWW@VUDLφ_O*w kHl# A$ I%u)DR6ImT̲"• HfDږuŵsu$ ; Y鳷^jGTk+B ǯqߌm}v&`U`x Ȅ֏)'OqX93/!|C`V90Ú?|?ƢY煞±o$c}hX὚_j!fQnt;zګ mBL}p9܀/r}Mt4P;jsHn_Gp_9e)߼#wS=Rķk۝ >y?.>Ir%$PQp{n6 ā`dTz4w:@ɺ$#o~\b(c-O˾Ker52"w8)ֶ%+{<`J'V,C+K;.$SxʕJqө*z@A h37 w|WMEKi{Ŏ g#DdHvwI%[8єAiA` $ (c[;;_*Z"ۗy8"\.n\+:7 s𮖛GT~`>BabR6/!-w&g8ǯmub/.η>i>S}>l*)^rN }wiw"EY'N;NsRmV{50#?u[i@[=m֥ rQO`ܞ}E 4Vԯmgcu< ]v S}~]u;ۖAJPQվ#X.tW=wqu_ftRsd`!PA3ދ+jZ)XӸd 5r욃rʍmONкjo—3y"asrOGCW]Nei"0 }9ҋ EEbh^iŻ1b%H1.޶zŠ(/ZwҼ/Zw"g|2}](%̰%a AZ?䮢h*5Ayƣ^jJ2=iƂ`,6т@tPN2@8FG#PVJ1*B_;b˱pxmorx~tl@AG^>tsoWq?rx~tn_΀+Z[]q=Fl8b>T1Ǹ \Sѹ?:g٭4Co.7z}e)C9b杹?:7/@bQTad( )QJ,1*UPOѹ?:6!9(}~X%F,% Qrx~tn_΀`o `61qtBrcBIJ3R_΍%X6]˰NL;;{P>noZJ(w7֒]Fszѹi(޴noZJ(w7!ilve#T-Ns L`kp[ߕ ՄJ-qF͂Qlc>WqG6Ck]8+dAw@9$@d1?:څ.#z2F eA~).`ѽ?:)k>¡Yda2DusƋ̊=j a3^G[AN"g|2}]Er _rWQI4QE ( ( ( ( ( ( 9.<=4j*(bbL?ٙmZe_02|9?OɼZmM4>IB`rFQyGn@XN K$ 9#o)'OJSj}Y`PޛXe +Hb q<]fKVP 8 3000 1000 2 2 0 C     C   ;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?վ"ǡ[ۨf]e=~6hK]bV2yǽvö_\(9<~ϊ?~N=< b[jMTQlфbi#@k>Z_eN$Ge#& VGiO֤wFqezxHS}ހh|M\xC&խ`K Ƕ SVP( .@O)_N_N_N_N_N_N_N_N_N!LPQE3F^yjRuC^I$d<<7I]6}6NII ~|d/i'gmql(aܑW~C3[5?ʲ)ٷ2k^7ᗃt(7J]V($ڭ4۝3[˨2:9kV|-a YIW~<5.&>!2@<ӻ`nby'W?'+J|3 jb$2"P:0 ac])7=5zz%k~_44,m#!=ӑgŃi~ ,Q3[ +_S_CY4>3[1.jK[+rd-e;fyAϘ~op+ DϬ|M-Rq~sq'͸+^x4&.1D!AP2[$ C6$mmk55w0w'v0c<܋ ,5lŮ  p )GO|/55xWK58Wp($x>8__^FukKٽs}vܜ>nS4x{CzơpCWǾ}(%wK]KF?ˮKK"/3v+%sI>//uu_՚M]L^yyt~U8x~kL̶d{)|9c;sڹ| t;V}c`X-qO$wuO#𯅴|L+TJOu{Qar ?oe|CKR}7KM+ym{pP22g щ/#bc, )_T4 [Omid2kEy\܁ՉߊMM;XIpG̫j@n:Gwy.tmi9_66YIFU8ҽ.@[sUu?v6p%s@\¸B2y tZ/GsvM/ǔ >[)G>noz߀m_hַQkiMg5a|`[955 }6 }tZ{4HT y;8I܏SX #/>-*K[.| ܱ|sǞ%< >85 څW@>IFA>'GjZu}~s_J5;if~#@x1_a/X&+Rj~lFHn V?ei <1sCM.;ڄ6ʲ''$rs]#x+]kM-.+!܎S~3×l%#|K7GƩ.e1+8iBH~#|EgxT-3co`,F,0# FrZt#Կd Zu75jw\R2Cw8 r>G"W*aXR; ſ./~^Kl2Ky]5 `g F;N[n+(a"T*NN-}7?;o9<|nl(82[NK!sZ%ecQVX%InS`IE#QFGQ(ފ)0*Zq32Y;UEf f1PgyRy(ɩC3(h"s@&hԄIJ}74f 撊(((((((((?veQ@ ў4/xL޻oۼI$g<{3Z~}ooň@''ǀeFNM𩾎xce:9p^8Ch:֔P#2 ю6g5ԦۏcލYKW|}>|B_\hW;-눣Idui`ay~1x_I/]6ْ胧)LI8V=sc_I'xFO4лƐ;Z<[߂?ĚdsɢOh!&U;o.Fݸ:#?r]k.y%I}"$bJFɬ?_&;uVpF{C\r ~Z${koxKzFV"/ _Jimw,'V$c5}'7_½7!5Ư5$j;FμOsMw8%vqX2 Gsu꾂 4k +NmlKx"_DP? (@i)ǥ6 ( ( ( ( ( ( ( ( ( ( ( Si)OݠQEπ-kui -G 3ڤkิk.? haz6cO.fOYq;fꚵZoimyx^5ǥjQVNO:E2`@֢s?~L¶*4{~m;wVH aEAqۏRZ6sM_0zW=|Q}.$VIFb?N ч+Aʞ˹KF^ Nd54m0vXlz+jc )qZD}}(QE&-PEPEPEoLP8;S((6ѶMq8EPEPEPEPEPEPEPEPEPJ~%)(>b5 k /kOx[g$0i#b1d_c~kQ賗:p v[le+׶u)'ڻ*ea8 ϑԝ+,"I)8#*qp ixI&RY[隵kڏiA:$pJw7e'󥋢NJ՜_m$aZm$0 Vdz{c>wо?xxc:~=?_Ju]M7 5D#1Hǡ`)8+~~_/|0hvk:zjrxudm@Z[Ou^|3oA^w ><ʼnC$^[b&umA<)VL 6Ӟ"7|Gk[-YҼ(F[Y^X/* #s{񆏦xKĞ6𖑦Wlt&Ĉф!N# OذۧǗcᕞ>-.! FU $_'}kWǤj>ZVZ-ic# 5[P8p"{÷^?^ckz- N,D e%_0LH8]'oړUě kum6MN ^ kr%G|\񧅬q|?ira$$|/o]C^&Xռkcifll^(.l(NyOƿ ~;x-K:f|^} 4-'I׎ݍփy%ͭſbDe/!LsGaSZuiΓoOmm [yk?u㓜`޷~/g__xP]Zj$q2w3@K+־ޒQ|R4╀MYuGʑZ~2> ֋}]_jZ?*9&3i(F02O?φg75[O"3S°?d;_ ^ .|7O Z\Xïy1\wÇg,ꭸ`:@ςkON_|5;M[j.!a#" ќdՍ]%߈łȎŤU@ ^\~xL,mCPԭ/`fo&O40`e&C<%-W zv\yyT8Rր!|o|$>5kjrn9+ʎ'ߵg|4a#orQ}fe6eK!ad*##&K_<5۫@ In2UzⲼ8 ]oo^AsJ7 4^Y0;1t<OHFy: e8Y X(9'@<}66>n5޹e:GnD62FGp⑯Z̓X[ݖ0R"eb@p q/ TkN&XHgtI a3܊ƓfҼ9ܗxR;yY4q0iKFn3^ EPEǜo7\ÔBmq~ȇK𾏢kZomi5]AHdHR1vbzbW |- k-k`tD3RA=:~hY@m<;x@QxIR6ЖۏQֵ>L6ajhAu+ AA{|$м ^[նuqs,, $U1옾>=I|%-}lԫ,#kkDO` 83Anx~WS1٥{Y9_A|OW8"4~Psv?xߊaOxHc./txJ`ʢ<Tɯ~|*>R[io9b=}YmIl Ē>cþ;3Ï\zóQ_ 嵱V2RO2EՍU R'{ym#sRx{F4SOR0>gswƐXwKP+C 1ma0;2BU%7gye 53 jj抻WRր3m.x%V:-%n=Snۻ<-j)Suk;,ZG{^5Y2zcQhמHźL{vbdU W7ᯂ_ z?Siw7 ҩdhˬJ$ OZE߂5 ë%z6 N! j~W>,x'k4 <-yco4"0ą7'ybڷoO}?ڵfrKW' Hbgwx[~|Mh)H"bʨ]>֗i¶{Tt[m(oڭIJrJT}*/̟4ᯄ"C:G.0V3sgHH]ؤIcj_U;KľmsM[hDS pi_ep>x\͡\xIH-̐!Gmc7 D־kP>查xr6yQ9~Esҷlg}= - Tz-%ݨ''/ryNRh lK*5OxV@Y=Xq4ߴawU jŵ]TDɨ@K1lpN ]׀׼OS >AЭn$s0y\ܯpNx|xZΉ=ӭt[ic{fEWs!;N$@jh>^OQkztp1pLtdՉgmh_>}sLXdmFܺF8ver 'p/S?Z1x~&&C)21+~& >if4 9=KFFc+pǚԿmM/n|O{֘wH Y"xPޯ'ygaZ5k]9Z(f 26TW[ ա'<={prmpV+LJq$]ٟ\mo:6Yg%.щ쮥Nv8/}t?Zx*+YRByn'ߴca}k^5W|=?ƾ9vl4dX9qϲc @%PEPEPEPEPEPEPEPEPEPJ~%)(8\dW|lC[@z{% :G;6 [([kD-'SunCv#椢ѥTS>?|!kcƷ?`#ؖ#MXPT}^p?i]O_?*xo߆P`—Ҋ(6QKE&(=-Eu*RH9q@E,STҌޗ&A Z2&(b(&m80=4)P()pi(((((((?veQ@8~ґ,buWW9Vd5 FxFo#iԁ`FrG_c׶w/%nj>Re3xm~xrĎaBZ$Y:- FZK.c,T8|/n=oHӖHRhn$`J-|i-̱Y"i] w->j.$๾Qt5G8 5Jo{Cҳ% o(?VX mOox_ \kr /O-%k(.[a{Z m-EWg>&5xgkBO/ʒ}Zl#ּ#ğ$*zW|U&ΣE 9皛J"|DEZ/lK|qIik;HH!B1@N?ý/ƍ>ڗIc-v7-_-\|ȪkjMW2M>c7٣YL^Kͷba'Zfj煦ucDHڨ[bIm2LȮxmY[`|D_7) X\ݜo@xf y&4R}ב]cڇuѬ8ZRgb~_ɸ`d>0Ծ!h^(K&[;.e޷ *<|;5V/GT͆|7˹bp$c=qր=?ioC(Yc4wqym +Yx#i~k'Q!u9tڂ1}onB x??g_F= dZ١$ .qڮ??uok*::-|0(%F6^? #t{o&՚-:viBa HԌJ_ ~? |_i~ϪJe '#'k↉k:fks hb&BwN9$+jew>Z_xW'_Z[eR>bdz"=πn+Gky;_OJ:{/"ہ O ? c4i²iꐛ[ht:(Qnj~Ԟׄ?C7Kd3:jي|m9ck42x'ᵽς iv6vo i[1DNk@7~4~o/om>k 7!L;9 =Z_ %Q-f[11ܹ<69/)m-61j'@I 20=kv|] 4uxB]:[}Xoo$Rdmq#$Ewh+[IoXzֶvS\!Y>Z1\Gl #$ǫkqԆOxbRxg>K>~2xľ|>3no:Vc<>j3D@/ W?߆ºizr<%}cAkq JyDnTs@Go仿ve31nu 'kgO[MCU/m-.9.$h=HAۂW?cCE IК K&IKa+8l,q|<=}i1Ů^CYk"-r4QҀ>!D.o cmg,|& ';*c#8Zy[+ cr{xEok/us2x+[xgTAS}uo׊!mt;ձ|xR~)Gy?m ]C$=쐌 ֻ=owcj7\!]J4 &HlLw/ F3y xWH]3ڷQ&I"6D9o:5zN%Ίll4lSm͔lyRpr.?ixxSVw~4渎VK2 kX~GNy#Ꮘ1f#~&2(MxIaGOMOA4 `k2&*uڀ>)7 W=j4]7GK}],mksێ:uox jLQtԒ_H?yszPxzS)w{RPEPEPEPEPEPEPJ~%)(>Y!(S̺ΕzM yDQԅ7"U|ҳxF?T2s"g ٍV,%qDƔ\!g1վ?Ÿ פƯ|Q_[MhIut ĕc־/ț`*Mq]ͼ\zW.KiF^L5<2[z?Zf.wYJs+fTEh,ೊY^wH#嘁ԓ5zo'(X{PmmoQ@ zSYBV!XfPx*A Z+AEooJ#aUG>zZ(0F) *F#ޟE4>lE4 ' `Ө4?>`/Bh(X:i@hiPM#(SEPEPEPEPEPJ~%)(8MT8d_x]V&8ƕ3Fw&rTJ-H엒3ypv[GCڷ>^=kkFKȠ qZiX\_jQYY@In."Fbx>7w OWa͜kl~,~څzfiz2V*0!#|N  t#ea{-Ut ʶ(}⟄|Dc^%u%xl0y!y#վ+x;@ՎxH0k[In O(, ƿuqHV;bYPty~ kzQ5h`MV$mF<}ȠOEij& >{I NN{zcxd%~ mJL+moGsk6 x +WZGgE`.u0;¸?9-o7ހ>>9XZLǚ6[: Z}όKY"WKw^p|/+>dxm8kftou _-_(Hy1gO:PSgiP 88Ixڄog[5̶k#~|]ōn+M֛qF Fyz57+ꎫgiz$W> ѹT{UZz/4fU m ( % #8~{y8 Sӥ5Oԙ&x?~si:կ5kofGS-ȷDb!L I{5 ߅[MoQ"Р\ih.bݵeݞ kY6nP碓M[ٕC'?5MoT-OF!E^U:(Z6 $鴏5[Ooԏ#Cxn >S«YB"Ϋ)kzS[|IoFm7EJA[^;Yx:WtCWQ'.љn =~YsF>l@5dS"nq~l8 W3|Kд=wJ`ƥ_}el-P g#$ʚ[};Q|cޝ7Zlj׫Ql EmcKI:? "궑4PnQT5'Rv]݌Wɴ]W\l>+Ok3 mKv|,o2x#~zF-֯#C))oI#UXpg :il>x?U5-FH<#,P.O, ႅqzf|Ejυ(N֎uyo<1F0 tV7M"2 ;#]Buݑ.?|95 >jG}2W+ךapJI+!G}GYxn ~3<>XAioh95G=.@]o\_JE•cïZ>9bM7gP<]."F4{!13d* ]??ΧoR=2JI3\CBϧZΏ\Ct$$HUK#I|q <u)?,2jNAc@PEw{/5|@d2Z}cͷ<nqPfǀ2N>yZH?Z>ڜk !{뀷0Ka\o/K57Z'.z07(D,@]$Ew:.4m&Hw7 þ9S G |7r׳žʐ\I7β,095h!)wֲ N%뜜8~:_OM4R4Q2Mr^뉤i\ `քw4X'i{?bڕ^ͧiAYKۼӐ @ھo>&\W}" gʵm@ETH@bx.Z5`39$QrY 99Hq-ݾt}e3Zy cYQ*Hr"ƿ:wu__Vn.[M$񏽓_@ƞ xźlj{A%0n؈Bvs6+_i7څy";t2Wq N@3KkkeE r(aO 5ص/~'7eA4]}?4.gk_%#XӼSOOo ?Uۅ#{4@ P莁H.iV7;4KW]dxU O =j}:7oHӯtYRBVdr9R_|2> |d}_-+E.tXͼɤ[ʁ[z{쳦V?~4LQ[K$'pL}C_1 wxŸt 5`UeVDondkcXBLk#O#!\i5ou)K?5촟KwgjQ $Qsc F1@mt|eaZW_j'%h42\S dרΉe]ǀXfÆPz|MoݒOct}v$Etc\!8=@5ùon<5٩˧[=JbRh~(;tk7:ni yrλЊ?7z<mg<խѥeb۪2FkSڛ4٨{#c*$dӒG'u/O9`tHWgs2k B G PnM{Zz8oyE ARSU9U[k5$Γ6R'yO+nY/5O}AFպhTȣٱ~"~^jKNTq'tV WyE03d.֯(]4*etc5lXy^Wo۰cvs5=xVO_ZGïrZVc@o/cO$*οF#"!8O8.&ѸLTPmtF ֟ks q(cgޖ@/Eiַ nsa/Z4P]߆4@D.;6mL*]BKVG\B*j(v ; kX(UP}GqG,k,mO+co1s$r>4<=>-g *wZP O"x#v,J#Mk 6ܠ#-VĻ#!=xL%ӭexsBNN8(6Z@V:ׄKCesk Yc&O-@ï{Ú&k?FKk$1#Dcx4x{Rѵ;U5 y-nmߤ:eA5W@ޝ4ZZ^^*T]q#[Pmt6H-4[Xd;(aUF>jdҬkմo]|쾅Q@x{LQMBM>KnZ%2/ѱ ۩XXԒ"zIEA-,&?ޏ?ּOc/-};UK=OH vHܧkyL<׹@^cii֑CeImتA@4zUy)w"uetP?cn<= \AqRX PdҥM2K8RݠS5KDmXeYZP]׆ M yQ #I WAR=1SQ@a]>[B2ZNkYHU Pp9zӢ3"ޕ #G@8sҟgumfbU-:օDw|Fпu$ztMywR .e\2:]Tq@IHE^4d>ђS[F NO&,q]x_CNcMtʰͬkA'blQėV^,д6Fz=|/EW[BZgP "3k܋'X{c#ٞٿV^9W4j#CX瑞Jߴ6 :y@`oXz!=0¿= P7ڬ$@@'_Mk Z 8H9N X5Ϯ"2 φ8tA[$d7)I2xcQMtҿ*y$D?ZMƘyb  'ݙ@"~.QuxL,}&SMcuɚYFRͿnEf|K2'-u[IeȸQu_--.-(gbE#iH!Aዣy6PP[Lk)Pp9#fO8fTtȇoڽέiv}ޥplFDvfCB=r;BP@ Ə0W箛Ǟ<{-gzΟm?TXCui߄{"R6ppG57m>k6/uiGOqb) ˂7A>YCG )p'_%7ּ? Oxğn:>PVPbp8 5ߵu 2AԆ>idž3\ =PG֝cI~ ZV??gbj:{T'@ci8;VMVY1 w=g=ZC&[YntȯX2AoeּA/OkZޝ_Mn.%{o9@gu> W&xFd4|']\RLh$HbY )r$>)DW_ W㿋;K垑E>q˩}Q!ݰm8\ݯo0u7JJүVxWR#  'C##,ր?DHdƶ>4'ﭵq\]i]##Ŝn,kCX歧hxKx iHdh̄32h[l{+TQԒxi+5mVMmy@̑ɝqٰp}[x޻5_W/5K9.X ϒABOQx5:OA#q<@>r=sJ\ k^)>(k? ɤjeKtWQ\<)vIk|ačG6vB:-sK-?8d=}qFw ZςK_յ cO.t׏(PfLQXaМ چ ^0|]\i2$7w ]Zѯ͏ahLmd>nAVw_Z=RoM .Q{`Eݦ^xa3 mLxxqװ߇ؼ|Ck V濨iy>LA.FS4I>:_Aأmqp3Dkx.PI^yGGopVYS%N@wzW|Wu%wīxm]B-{@rOldFĪҔȡs=k=CZ藞$-:ORxf H#m\+|'V0/ό{8O]gsZ^@f"0Z})Foޱb$G\,|Hּ@_xŖI-z/m +W!fa@r.Iހ?Eplw_vi&" )`yʐ0b@`k}Xo/NMPFc /~<kZ"o_C鏄_߃o+vry?vj5ZmKYK{ɏ#G$r#<{v&@ OxKEċxy'̞WmNV2FWi_׋][Zu\eA8qp>Sz6K@|'uO ]Io`t‹Rsmm;Er,wmn]]YE+O^E|9|-xP{Xͅ[Go b%Gi ;|T.+| 30\IOZW2r[`d]Q0 xzLjb!^0`ӏf֖ dvRK, /#ğlQ@kῃ{SGe{_9ڹhLgKOz;kӵ=1l1BH]99[k$S? !lt\u<6('rX@Fv!y^@=E.y7|5tL~:Dm Q@T]慠XxgJ4Hl4X-`@ƃ+Bk ݧR7J("|4о&~چ+i|'g<԰9E-e Џ/@$ΥtR߶6Ïx7>5ӭEW9a$Y`}A5vsv;̾kf=cK=j7%u 3&8ugx*+H[<0bu.0Aai+uPPG+>U"m | 3ְI>ZO՞ h.Hu5-H>-GM5ӧiz?gIUyw#㋿ k/wR[Kf#ȲyWrcjn89mQV|=kj,c |Άtr3 8dfZrԼ]|xs jm4T2Inrn9UÎ>OJ~+~Ӿ5w7UH}Aym$Q6]FhBgZ>$x[my&wѴ4y&ki\ TUt[a-GXb|S^Ky'kv%p 2 phVHVмyX &i=Pxnk9᳸clMҘ’Kg{g#XMp!5JEݖ-ѩEe[Y h s@3SG#Fp S}*M8ȯ>~>ӘxgZ֕iWz6w/<3Cx |{oZ-C~9_oXc]K I=h$kOhx:ٙ KQMmE-12`qׇ~/`4iW__xITU#6*C!.7?.">־3~:|ex?L<}躍u蒳3[],I,P<ʲ+iG1{6u~+U5ce\j]Kepa2[Zv e.E|u>lnM+Qngؽ| NzOďh׈_EZw>jȨ~YU@g%#‘_U~Ma9-ahio勻 !QF98~qֵ?zW}Ne-[|"nA"y2}(2L(p:'HӭM[G^h7Ɵ/4eծ"Au&Vdll/<*u?]oD--/XK]6E_͏ >lq%IP::7!}|q+T&.uXmq 6.pws{ :GsGbK]>.2bN>;T3Pn|Ι]GW𮷬Tu^j$YZv%B\?|] 3Z'3;єP޾|wGj>__jv-ta0X($ۂqgy) ik.:wkϩ{P6 ͅ @dXWĐ~ӟZF]E#C{ ZD!FӞXs?_t3𷃭]~MeVK&Hw'Vj'ğ,Ũ U?;CIS5OپݤuyǛكŶE< Woc\H̊X2)W&d? Yj#T|Ejmlcֵ-I廵( 5P~>/gmSAԮKMSK0A5ԍ%(P:zUΞ{ڮZ\pM%ty䕛4;^Ep>i4=&K]]5m*Hab (,Rtz{-ɨܼ:8bFU +5'໭+S4O h>~ד\L,Osdi:%浠OZ~sr1K_O+CP(EoyfmɘGo4wgyL~qs\GoUDzUxc7ֽjlmx% +zB}(柉dwxGRxWcWeF]{3)媨X^ӴNˤixons@bAEzq@Nyb<+?<6Ҵ}G-*]s${q{U+Z}%ԉZ_n 2.@$F1^۰S?c_ hV49xW|36w#?d䓚'#jiai͠Ig'lgϻ >P|:Zu_ .kkpsǜ $NMz~4(QEQEQEQER@ (Os.sgjb^40A Z7W1Z̻ ;Erhr0yhï3!G.w}w= ޚUu}xűx?EP-8P=|SÿZeuc* n9lxRg~=O5>1oUמn>й =Ce_(Qq*ܰ_P; S3 hSqos6O=hxU_͞E{\"05юဉ@ڪ|omO gvWƣ} OG֑l5UwnIn'?Kcsoi{LT'yN^"[rucRwA6Aa2+?n1}1E|'x֖~%-o.nVV"X$ln_6p-V/^MY25i7MC ݎqڀ>JmK4aamwQ̗]dBc c }`\c[B[x|DOMoZ&L^!/k#:K^֑yY]7Cl1WGAA=(z+_~պ煾'XhZ[Z:wjلd,[o8蟵!-'HmCVkK;x-|yZ< jb}EyO~7xOSUu)tlnձxeoԂ@<+/oG◄|  Zk EadXbF8dE|n/3Ŏ!FZu8Bnf<+*φ?k$ִi@GZ8,ڲ.Փʔf :0x<'Oψt/"qYZjwwR.Lj$ 8x;Z{XX |?kfþ>N-'mFGj@M}xqL$gwx=>"]o>ӣ='JWе cngTn1)YT` ﯿjOA{i|2kk2N*+OHXc 9vL@O_9jmŧ?^/У<1\R3@NӉzU~;yx+Zhr,5x%{L($\}K:]oxr,D%#K2 H/z4z$:p4zJC#%i f^{ RkjRZ+HTnHZyhSCL(((JShQEpcQko V>dY3Ǡ\u^0|5Z}zk|(-!9q]+_ʃf7&wMw:𸇇s/|?x2 F-EL<6U+k1Z%l [8ǵs<3}%}džV`~t~~0~w|1s×'󕷒ɗv79S:XT"եWT`"L+n<rG$k!U** 5]!s~ˍs_Ē24h7[ B2 hfEI\qwiz@)q9nk)ٚ]~ YffYA ٞUdn':xWV4Nk-rktϖbM8ed`W@/o_t~.k -/ άc?+( OCX죪CxVӯFmnRo 2XݓҾ#4=##h5]yI-H qV_}jUY uiRIps͉}ҥA5~Cjj26."#Ɋ DD{BrNk~[9/$KOTlNPl])h&06>WmeMvh%q8 QEյ_;YҴ˛3Lu,ʲ({KH$_Mc4+V֧K%Մ#-o,9pM7_uxI,!ӭ4Ufkie W4P+5{Ǘ?Gū+-N!gc(J9.n֭K=7k]+{dYdwZO ިg #t{!n581 ܰYE|a+IK|?-;F&5Gf`ŌC}޹ݶ5MwYn!$FV&ej,Lb|\pOɫ>9]ͯe (W̷\dr;W}E7RS7m%h@ E)⒀ ( Si)OݠQE>!CxOI-Z$Mב!U$If=kN]5#/idp76|Kmŝٔd㟴GI_՞źwƛm!H $^6~|VTS穱thWTh+s$Z}᫝"[˨.}7A$}k'1#? /zVF| |Wwr)#E!A_ /e<+f<3\hNfUrZ߬m`5V} O/5S;kt{8o?6p?lwk1˯_\iQ;T|hqvh+K-A]+RԠ"˨'`鯧m&[nQ;@BK@IQ^[xz\4 J tdy"!nF .>l |isiϣj^" p̎:2u=oσwzt>Ha8\.2H?5R)+!oj6G$:+IRo.ȎP}@Zs_-w?gu`am%I\BpuUH״x⶛ݟ=ɝm=]\ˀF噏zhk}]JLּ;cUW.l(ͣܫi]LR2 O~ 힝jfMBh#W,8x@E|sbRJi>Kۋi|51Eyi%Qpy*ewX 1ױxxzvmiy{욄NpRD=A OΔ_#χgдhҴ=Bg`*dA9潶n|}#ӵcj#_28Q41m%b쪪dK+K{ĺXGP,l.tl'Y~}"-ikO~/fmOh5mekO(/R훠h67~&мCeygi'fIo]X$F+2`><5kq{ h s&nO~ 7o<17Eo ;c;N(I `k|[ZhZ}Śdzί {lM$zG5\O]٤Y6JeW}@ǿ`> ~:ickW*=SH$pݰ)BdRB]?hxoCt 2{rA,*!PXѰҀ=6ik ?^xW~viYI!H#k$6F k].j%o+%嵶k}hW`|% @D^7ujG~0bj[!}Fݤ)cv;˲/ox?#x<Z}$as"4nFAg =Hw@`SiMJShQExQޞTS򞘸r-xZva{{=z:6c{;CJlѣQ TpF8?_[g5 ,otLRkKR1: *=Sú5WⲼdkЗںN?Σ  Nm0 u&u|#b(Wk'9Z||:mJ0/eN^ \'Ë7HR in$l@j%<߉7{JtcwVo9^?>-t.~QX> $h05 +Vw|\۶#.[mB[0RS#? g3w fK m^5^)澖u!~^? Դ1woIw/;:b<ߊ_oٻÚ ut@C$s8fj0 A#=+S%vV*r|S&xDz^_@!Y]̟0٧\I _޹h>(zž.ui|Coby>idY@hQ_UQ@ +Ɵ u}6:=Y$, PHw8>w> cGҮ4jI"snY#V(уH ׶Q@~1H&ߊMgڥޕs%ťw$dYTFEvfMk_|-lZW:JKv&Ճ zWt@%wp0x>5fVOg^dQt/Rnpj ~*𶅨,:y ՝ȼimeTĂw _/񖷥jrHt ua}x6Liy#)۴ 4__5/ZlNn."2f`#T* P|Oe{|5m`ºĚL鑭jc< xgO_> xI|->2KonƔ4UXF U0[ WX>?G >x?wbZV; bHP+n\n9>\Sx~WI[ef9 dn^Fs^GJ^?2*~M[\Mןy2۷m@gș(U_;Wm[{]'[Se&P-D FNAAh#$k&Ӹn@ۯN׷w׺7qK(QހS ^g>Kk70GGR)hn YɱsN3Ϲ@7y)cXXp¹d rtz}OXŵncRKtw&lm~싽gHҵd'dm2+ehA<+|c/|Uexk#,u 9VI'جdR[i`E}?E|jb6N&6t8WYd2RJpp+} ^;eށ:C<%Hȥv6\}SEpj+?.汷> ]!'# ~e ǟWg8M#e /.UϮb4q[13@ʼnٕYuطſ>|I9mimZwt':SJmR@ (pGfxź/?j>J#EiwxV"ۅ.^w85$׀x^};?ʼnNs1m?0܅ 1H2 | [kpCĚ5=z?>7G5σ5Nh0eW1g0~mz{=>VGa@v \O?M|[==:L^  ]B^/-"Hg§62Oa[uuZ ip@DzPdgkͽ~sȞ|.GOQ@N) ޾vDms]Z][ɧ I˸#YBgflarkg#>"}JKK:FJ} z⼫Z~ɧZMrki,4 "),xgh ]_ö:}_D&)@▼'ş>FYYh70G,deN%bkO/~{}֭bl!馷>vՈA< Ry>T|2eXݱX =ohz}md ֥kbn?} N ;xs@߶7]2[[^+lCi7ثBf ®گxjqukK)w<~@O3*1xQ^U~ h K ^W`YtXoLjZv n=5Þ(viZ34p<`k?ᗊu;P%_x3>p|ЁI z+hbo&=RKuQs}͸t<]W\[%T{xN6¸p-mM^/LJ>idĚEzt2i_cF­T#imV.YZI 0q^/5OS&uB$+a{n}x(,Qj?A[Յ?`|/_i/C A wi ,-*H#߳/|! c[46ǽ235ʼn>oV=WҐD:{ǺֿOLJ㖽hE=9 [? h<;x{–.kWa*76wqTu&ntZCNMZ h\|e?hxL  #7W1ƫF"؅vgs?0:VWM7Mj^v~x睥yMLu\D t>-i?0xKX0%] IhV{G/Z B HPDXĿ>!^xz8woJ|5o{s%Z$GVw#H3@@1ǽ|Q~^0g?t{붚fuH+[^tyaEz!(wû Oc&g7s6f_=[ʑ@%|  W[ҁP^.Ѽuڃ_Oh:4t{ hnyD;'xg/)%.䶊6y~6جOA_H|heᏂ-%otm_Hb ''V5uR_5BL67Hެp9 b>GҾx~F."A<8K).n$Ir v|,:c.Sw˛5r[[&9'`"n7 q 8`8ϏZOگ|1wu%LjE- Mw :LnH8>UhwZ-%vkŻIZ`1@tk&:XQ2 < ##](((((()Oݤ?vEP5xmc6:E+L֘OTNh8,m4 H,-Ta cE*/P΂ѥ8U_Úpb3N4Ov? ;͌-Yp1F{|ΔwC UJ|sԼ'Ο/t'X~+l ŢǞO`{M3GJN]_VK&yH V OCLÏ^ &>'OHg [g ,-WK}.#sRvOzh"SGmb:wX,xQqK߉3񟌼=imOeב̭h'r8 u{|A0k7ic_>ԵKs, 6 moJz+>ѿ`4 r-n9 cޱ~ |]]c~|!6VEB<. PqEsQ|KwIm%^es[Gޏ4td1GxP@\OUmC8ϵq41 Ö5K*ؽ>ZEfg^.I=W7|D>!Qw6Oܿ4 U 9@ Њ騮v?5=FO&u}s7&3TDKiNWֹnI"ܠs@QM=hS(ɤ(((((()Oݤ?vEP#zW%S↝F Ρ8eeÅ'`9&ώ o&GbZȪ=0pJ8mV:}:oEfߐy9׫KVtV#Y'9ߝC1^N=4<SVy6}v.GؕQLwZ|oj:١Ho[:×ZZd?+ko^|~ml\C)Rs_9RmT}oľ[/ } S/t?VjŔ=gF|qI|t7×4dFEź3mR&  &xH1|#? $Tfv,=ݍTbc9>>Rú oc˰ ƹhwnş%{14群҄h]S ] ?ok7?4Z-WRt.q$\q)af{~T4A<@'ѵ<hW}'\uoF[SF>U ?85Oxz/[ŷ47i縻h~ԲCqeUH%PWivo-i%3>zͿYjͥl!P42ds$FX00ͻӼ97ڞkbP4|=xRyuf 1gqײe`۱ Oig\Q^(#1"c= u&URDkq$1*49,p(oO =Ko4 /N1[ >6+V 8;';DHٷNOl-nuhp5.끌o-G}zc8Y| ែ^՟m5 ܛk#n3f?,)pW[4/~j:mw ֡xfM'Sd7 ˻nr w $ȥXzbE6M#QEQEQEQEQEQEQEQER@ (*Yaԡ0]Es1́u>6<Q vn4 jbti!@HYr9^w tu-KQMc=7EA\dqP ~{UԿ(Fn1ap.fbUWWÖ7z֖ƍw`n6#3F={{3>ؠFB8U'j?vD|'o-0zZֿ{|,(`zP\O?śK˝ =V8cSJlHڳuvo ?:}{'O\j0ū1572G}ְ1v$xEw>^M9&ݥHL<0AA4%א'SM>IEF%Rp#(J*?5pߵrx<TfU}3jQҀ$ UX @fQ7ry:ɝ4&6?O9{fKL2GʠxDu*09@mm4ȠHiD܆t%) PEPEPEPEPEPJ~%)(>LѠ"ƳhUvb/ v?_j|OR4}Zu ?K5t?QUᎯcri|)Gqk%F=~Gzm}{yIr=\z,_1_o)S峪2wEa%u7u>Gހ9n㿇ŨX=B#@`G_ ?' &E:4+v[aֿA<5 +05$ _/#v%$$5w6Z}ZM~ϰlǦޘ(o_a!Hxc#Lsy$v3煵[X?&5T&IDfL'8 _~xcJMqO?էHYhg(>x~?-zPQ;-JBHRKϽyGvrK]Jg}7Yu07廄 )!CKMuk-D22:&gk86?.5QmbyP\oTE|* ҿI,VQ6bX֨`\@}Pcګa4-:٢s$m !V=H pM| E|m`yC\-]YM{-/m5TAq^;>/{_Ohͬgqi3i-g,{GFSM}gL[eXZ &-<>'N KլR!{cvRi3@wkX|2扥O:;[vrSu>lZ[tyxh4yx/M?↝Y^Fm""IlA84{e[t9'$hNo/c RxK]JZ#=I Ufz4;H+ q#CÚniHC:\D4uOJдh:ƿP&PCP(I}q>xz?~9B==b[{ [W(A ? jwVWh-WјjDnvvH]qq4ʍ˞=(Jӏ|Gx}VHuc7J*1@2:s\_ŧxZGxwS/O,hd!ؑDiT *:]"#[BUz-p9#}eZöNo2݄4vI7'z5[kK_j-xk3j!.`̷Vk[#r3^Di''n|?jL-&48VIX}8"Ӽ-rZfMs'sү\֗i djO@;=kgFZb.ܤ{"%qa׺|Cҿhh~.57oy|> #1ü?3.{޽oǞcP_F6,qWڻ]vi}21%3 IN#RcQK@ EPEPEPEPJ~%)(8X؎{o|'AF&oi 9=X-Y:׋1ወּIi}ȯo#= E8P\v"|Ob? Ӽ'V`xffy[ڼ7֫sm<762Շ##Ŋc-U')k'v8 BRQ 5 E3,8#ҀEPu?B9u+,"E$"¨'$; E:hl FzP覙1:@z՞\_71Xۡk j:1jŽwPG4."GFʲA(j)Lc(2`iXw~7lo'l-(%[WIy\}zhnJs6A#Ҁ&iA$$)^@@,.$e`zGZO)~+zާc5*Gl(9P zwYRFTD9$J|[xVmC$R\$"*YJFGpEj:g^c [($ ?tWmR˸I '45y4mN8wYAc'uH#8 *-ډ7!xRpo\gNq}Al E49=KPgo,DF dOZfg0X j22HAp4RߋM:V+ OO586:%(5욻>6昼_-&]SluOٓӥ5x-X\3Cqm!B@l`+ֿjZ^i (X'SwYYjZ'|/(4 >8W-OA5:'ŨkmO-`kx`]a0=kΡO0~Νy3ۥK9Fznj:,W8 &( F '?sҭ/ Yiv$lDU=kt1ھ^);{۟x5H oME퐢\ݱ g5=kkrxPBm- mdڠ7 0#_zKR~k~"ֵ Y]̷7]9g\bSvWxoˣdtoy֘ϊ|OǟoǷS[wG @ޔHI$ٽKX_?jIgORxk3^vϴcgXq|K|Qo 'w 7z>Rcp:~#O>KMo\.;hZ}B Ux% ̠z /?M>hg%$LH*pH|5{MhH '<4>i̤$/w_^|'G?%{})+?d7e9Wxg;Iеo3Ceb֭Ciq IZ[pH[(OZ+&SόҮ~c$o6қfBk& ׼3i9gb'T$T Rxݚ7a[ǽgVׇ m'ݭOd`[1W[#xNsev-P}qL^}Y ` ߳WOxMoZ2*N$F̌t$T(x[u YCPeHլq@u4?~ԴHu_i3Ij"ډsⱉ6r$f;j/^뺾ixO [(%T)fP8s}h:zDwZ̟(AR= c+>ZxBkkGүKK2 K\ps|mu'kKxxrjw,nl׵4YY 񶮪̛ʣYJ|[&|-cos 䄩gx1I,I Ï?Tׯ9,u˓wlf+4|T{|y?xW~5~f|Mmeqm+t$В7z~c| , m5hri;< c ɯ<=7|7𷈥4 Z[l$F@D8* P~'`ql4ѣAg?@;#?/xhZī5-uia náRY+>|^׵> .LVXDan8j߱|Mn:ޓxsGtւ&r(@6r+t GSz}4%n݁Gnv > jPoÝkU%rmI>|k?R𳦥>%s}.q(IlL9(AV²{ xWAҵ5[Oiz*ioFh<'5|6e֓K;im@ĄFt^kH; dwS]'ď^9/ 6q?6/^Fmm[FͲ[Qĭv=_ľԵAœΌHfx_|bk>)}),M5Λ r>յ]G"u/Gmahaas/"fe$#s+ݴ/{e1#?Q'4u׮]bl DžI*4m,, ;.۩FlbAsh^N;QKp|y5\h7zZ隊w9hH$?+XT6>)U^rd*,|z\8=ģ<-XK]+œK 'E5dVsKP1h0?J<23@dUxI!^)g]րMš]IրE7p_Zu':hu= (`Gh[b^)4-Ӂx p C"8y:ip?TRp :n==*uKzM޹y=iTl1@#&@Q@)(=bIarN*cUe{qUQ|ڌ]:l1_bI&>gs~V5l[KlRӦ*rВW71}/x*yʢSLݺ]R[zn[W0ⴊݽ=,~<;Cok&y0Ŕ ې:rrx].P|64}&6tmKxq `yO3µToTBRp9Hx8rGS_Aj0&F2Iq){JA_VToQg}W۸q3 I&Bq #"<%?A[?|icυ,k6]jLo2* L3/xƗ/- X4f{H$xĂ_!ݲ~7i t{7Z'|6#<8f_~U|QZGo CӏAx?|GAีKM.Rk{mSkmi-Χa$0L:_s l z&V,+DszIM] *ݸEu_|}y'ᆝj:JG%md젪/` TVl556]i%1g32mݻp0AƭcgPM7L;y5;V;=8+S#Ǿ18uW[&._jfb|.C 4LۏxYo4s|wsasuc6HŰU w,>|4eoG"ҞëlّҘ#D1ͨJO'J)Z]'4j<_Y˗r80pzWSOqú=7+kb?P#r|TZE_0K3H}_OmdѼ3{[y=LR#7Q0K]t'@#ᾋMmhjdlTsmE/?':֥?ab r3i{5Hoe{;+# p" 69ouKWkZy5kmw˶6B4{4/J`./o-gX:@~K6FH0oxO#'tWΛUMRTnѢ"eV;@=ÿ-?VIye5M,}+\1ܨǥyŏSG>ѵ`PJדOYdPeSXA+hmkOKMAB]N}[=ʐATdc}2x4SM}i&7lW$n\5ظtXu/k:ɠjeHn.dw6rsz߃3TR^m[-y#vc//z?gö6QM 6ëRv ́]O+xsGğZwGcwNiM%Oy$(,a|y !`nnM nΝ^qo"^1մK+ H;pux4w៛|+b4Zz\nq۝Լ<-/7wo%.3G038JĠF8?ğ!4=U'FK*\`18P [? :wEwEj2Go$qJ]C|$8UT Nk.tvMY%2{WҼ/BcoxB[ͅGĢYmPH=&h:4Y|_屓yoŏ/Y0h 궳I=4r;DePp g: 'W~!d2^i- AfrR#4 I#V}v7ܼ1Mvʹ<*&vso_$5ڋj^vV^&Aâ~9 gAI/6%4;HuofM[ʦM]"Kbw;t9Y8KOPeCtvԃƚ׊5--;bS܈ؘ®3گh䗟onȌ' TAڹ~V=+EgesI+}3^i[껼y-uztyAI:: J{VaK<% ܮryPEP(-Q@ zRE&ђ}ii֝@R1ZPr(h:cRKm=(@qAPMR( ( (Ƞ JZCҀE4։X䌚]CϽ84)sI@ ├RRP()Oݤ?vEPI8|=o޵zF4$Eglh ѿho uytfɞ< z_ڷZ"%ցj.KFBULX?eυz#Y<ݤ1(iFc߿yue7q>QQCNo~{ e;OT.II.D!D,F " Gၧkp{y|-%oRYH8_6oqA,s^\E;3 Ny52xğq[ijvn$JEF"S<. 9Tjii}g(V 6ǚW׫kn|mCK/-#av,5>?VZ֙wvVN%)ÓW썮0x@w}|H $IqG#&.34yw|7V5oPgoV:m"4pjׁa񂤐fx{YҥŔZ&*\]Gʂc >8ׯ| ^_ %cCƟ /?_=^:կ5cJjNt4+[eIXrv*(rYs N YxKCxOzV,@/0T.p3@?h|' JWH#cgDҝ@ڋ>ؚ֙\uekv-.8)G#` arHB?d AڄZܥD+t.+md, "3<=?MZl/imyX3x288zmv>=<57f- $mQl^nP m|֧M>];G 72|(@cx/s9:OZAg#i叔cݷPW' OQ@M8=zW 5ioENkKfhac#)wb /\zWg~<:]"WI[G3K" +/,3LH~Xk?2q㙼 ɭoAFKC׈:ubme*>`&8P8mY~ZrKO.C9eHd'BYs\dw4;Ė ǀEMmIݚ;[yGla%29 .)>5ωGeK`3/q]}+o[KDo; \i"mm!IF@Ov>%#Y[W^NcǯE-,rMfbgy##*3]Oor*__\ 꺾ghL[r@rՑKԼ &;o؟h{iawr̰udRǢ=wg3[M_hS[ƒ-J8@m0X~ D\ҖVKvy{oˉc8|V>xWxƥͬёv!L? gƿu 76D>|=s>yE%|x>xÿuPZWnt}?P|>ZEz0"?.]o_.iKՆh-"c34IUe@ 1 gH<)g'-l4_RQr#_/kog5 ھ=O6!Tozr%kZK͝ޛsqYXKlѼ\߹ `Px㿏tAOim9?~_:ԿG.%H !Ԅ\p>SPGxS =eb_lq=ڿZ[ݮ>-"cEܭ.LʆA2gio Ză9J%-Ye4rI kgxEe>w-Ɗʡ1Vc $?kv/>׿y5W%_6<5r 95Z!^Ӽ% ivzƏ=cR >&0|É Ccj? |[xX:C!x~W1`dej_|B5_[_i:֭7h5HNe{`R\Iqa}o.__ҡ&tXьNd>]`u??95x*%DؤC >ok. .]#ŖnhgAM[EKH%m,R!$qTauoDx xFoúKY$/<EAT㠠QumnF oSWH.+$|&( e'k&Zu/OTOYXxH\I&# B p_zs((((?veQ@g=%|Em,fa:`|ȯ3h }?Oq!C06PXIjXptFU\ֹч!Ukcƾ>~!L&eV7 I-9}7Z^=qAm($`8 8eݘV6r<($ t|~Ÿ7t#]gESSYrzӨ\diQ@Q@Fih (r8A t(Uk&#ҏ/ӨKEQEQEQEQEQE)A#tzPN(HzPEPEPEPEPJ-'zst袊fotoxx-15.11.1/data/images/grad_blur.jpg0000644000175000017500000006313112616075370016503 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-15.11.1/data/images/add-text.jpg0000664000175000017500000007564512616075370016273 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot0230#0100Fotoxx:paint_clone|resize|tonemap| 4http://ns.adobe.com/xap/1.0/ 393 476 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((Y" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?cRW3}UodgxSWL#ϖ3qň[ )7dy95\p[dGj5}ؚWM4ebF0êO<Dq4up+=&uU0V=p3;wNobQ{+fV%eX8hKXe,TUz/X=4nRV`ŐHr:i09b?*7AWkޕ63?e nNxQiWGuh-LGBB v~b䏗#8x^ 5 MCqrGZSIvE=wE,7Ur!` hX*\?*MmumgR62ҤxWpiF[,e<T=x_ʩ&\V.Qz/TG=hXz/F迕R`b迕ǢUKy֋ǢToU/0zZ..Qz/TG=hXz/F迕R`b迕ǢUKy֋ǢToU/0zZ..Qz/TG=hXz/F迕R`b迕ǢUKy֋ǢToU/0zZ..RU?0zpvhGn{G0Xe@8 \.nL,J&bq!FBF5mKR7c--Q{Տ=Ќ'lO/*U1"!9-M&]Iedj؟_Q?+6rjO6'lO/( ,>5鴈$IQX%+gh¸9 ׮k؟_Q?sӴYn3@jx{Cm14M9ucԎs]~B`XAmGkc AoFGR_H ܓϛ_κ= 9Bm }.(n*dg}H}U&8_lO/(؟_Q9 [M>,  5JAlEȳ-J(ާW[?bq!G(sծ\C=q ?grts҉>y#ya©aד@c0'1|{/ n5n  HoxQQзV[[ʿ`oR8xmtV=4Eqϑ݈۷'^Zj[ߍJ]Fc.0P?0|n5jACqpvȄw`6.F +Px(.d8N;\,E l7/'?!uCq=[QKn"rL= {c}ՌIǪLn4st\_[]>ɵkC822}kSD4te)"}y۞|7CMk߼cҷ}Vݿwh{[~sM( eUaUXEC 4dXf[wq(rk>5A5_ZAk(SgstX}+O ȅ'Ag?}M:kչɊZ00s)qk[E쵫#uJd9F YXuA<| whrOiy6oibR 0}k|9WeuL~ۤAP䞽O:4k8)%Y.V8BFH{E!^TW_4+[WІT7YFϙq> -fx.'#/9(x#=Y=kϺ͝mnn1֗q'f[Ky~xʩjxSƱjS_l[oV~ߓp)_t{K#[{yK֫ cFOOL|IXj$(ef!13ʞŭڿ fi}{HFH%H0}3TyӬn佶0*rG'R].7+Jա/$qϕ36nnn,#+X$X˄laf]dkprMgl[;g+AH_ʆ@G܁PfL79Z4-.XrB$mћ"toO/w?ZvZMavt#'?n?4DYɹra;fU/ 6r#D0.5خZcEGfY4ZR (ChW++FQSLuoxuoc{ukŊKUi&xϽ=.-lw0(((PQK1@ Q^xP\S/ZSZfZ{ 8*qR{([V}~!]e}cn5iIJm3G9W?=rJgIEb3m6+jVR>iqZ$0 2yrI⦛^]?OfO62:n.[wڭ(UCpz n^%?brUv 2'np}z&w>^Y5(&&!֝rڭ(Ula(tRc~a[?V/E jV E?ڭ(UCla(tQ`o >m?0[_0X.c~a[?V/E jV E?ڭ(]la(tQ`o >m?0[_0X.c~!?V/E kV E?ڭ(UCla(tQ`o >m?[_0X.c~![?V/E jV E?ڭ(UCla(tQ`o >m?[_0X.c~![?V/E jV E?ڭ)G] j*Ķp @QKExaF[6]FEЌҽKT1.PzRc=|@jOڗP Tnt.#dS$۵W%5O9)/ :U  `f_րmZ?'_Vİkko#e]̙?+9]:XpNE^7mg8v t'J8T˦X$u=N8ǰsmZi:j0SI\bAq]>d7+\=QF(Q1@bPEQF(Q1@bPEQF(Q1@bPEQF(Q1@bPEQF(Q1@bPTu?j* GERR7KX|_J} #ST*E>`.qN}1FbFXtXS@ ~Z<څ,gI< nr?"yk눭.X(2j(!7C:ӃҜ&b_Ac{k Kcqj20`uwc!7Cѵ$p㸈1BL2QNhŽ7$E±Ҥ~T Z(k\d%\#eβtx `hGsJù:^(-Vh-a 2m1lU* ^&S.nem|TGyzh}MU&X/zx=/RVe^Q!0(_ Ƒ-*RV0;ҽoq o[]?ڔֳMD]^\A=w>l>Ir;Vg(5+Ū}gյ_E={=HЩI1py3]mu cYNXΜ#|F8qmPY?U+Ygմ⼼32-Sq!g$׮ܝڥpBj.W դ !#ؾf1'$Q},n\) J\Sdϕ&i=q^=xO=է]ZH7F3ÌwjEs.?5K)afy!>`b[. !市9p˃3ҋ0;ݧғ^m`ZC5aIi(qGvbMڶ┶ K"H01^[ȲKePX霁9%y|SlN//Onhz%\Li* 1c9}l3ٿW_EA+/"&m}WcChmJE uݪ5yY=V@XeHn?o-C ^1Zwf{f901\O ^1?4^`TF'HpN;S{kKXgCbV'4zD-+NfRS.0` vcsP]fM*B id8& 3Fi)E"%uY1DBe,@c##K'?Mna>@>L|W>mj8m_7ԨhTpZ.&z?|b)?^/j7M7L&;-4 gOz*'N7]>L$»} H e ќHUmáz֐&U ݈% 2A#>WCfXf^d󤍭Ы'ާKkKyX2^;T(ĨQ,1 {Sk_??€M#Li47idY-/c$v=E Ѵh 6_gw gv3N٭sL 6k_??€-6W)W~ c g#2j |Ck9 b2ZۭsL(٬=/kL;ijZUڑ1'Ӑj[})NEִk?K(٬=/ qy>Sheiynz=ȫ̷I-s*ES;uOmֿOhB-QˈȊ!~q-` aJ}Tti瞙}?QYz_@¾6Zh̊,ò6G@}.|5̓j ]Jq1;/ОYz_FgyFp^\%2FhE@O+D-44O Z}IlֿR1 [V+|~ER3?/Yď3}u=47 ɖ i=DDID'@(—$?)TŽaZ5ol瞗PKkYb B8U΁`F3jngyuiԟ@U-4Of3(vZ}IlֿR]lֿR&d-8€.UnIgg#A8 Zws:ke K;!E?Sv_M7 ER٭sL 6k_??€.Tk_??šYb& U4On3(@ gy]oN٭sL ITCYKK[)nWRq?΀-QEV跮/''^mg2( @TdVQ=)v\ut6)ϰ[6pd>$KƑr0^e%ؙvo^7H5 //af ,S2$N@u5x7H;WqL~>8p8bxXռM [KJ/\#Zm͏@eY뷺<3` zg&՝H*҃ʠ? VTsi{I7+5;Z\$ \ȑW Oմ h$G$#7 5-!m*MMXaxj׼NyqĐ܏3T} SuhRj.=1F='tgÚz M %\h}#J샋kdz ^ڑbTMM!TMMC1Sm4m4(MѴ86F@TMMC1Sm4m4(MѴ86F@TMMC\TMhƳDC\6dl5`kzst]X:Ar=B:‹zJk, 8f$9!uCb[%jڨ͞íML/ťޝ*Mq=ʴU<Ҵ}̰n$rWlr^ n/I%#HLzo>/0fIuCk8 b|Q^mtmmmc|O7mmmgM^)w61@x\Z/t%Jӂ#M`К}kE 7:iِ[BLsH@$OE{n4ooSNa~_p^ M^ĺΫ,vB8m b cwdI8I=i6 + iAԚI!M+j@yj> u{*.LKm<֠IA Zжv9Fg^ɯd á4z<_SG9{5fkie~#aS^֛]֒Z[xBW{jv羐Kp`0 ҝ2յj?Ķkjq,rJHAF<jKu%uIk7Mav"8 ׆zTƖ(58ƣ{,SLl,`sutt'4u=U"ɾ7I4(Klrjf_6چ\[)xcg ަ if[y>,&y!w}(x爴 xzlļ{y>~}OPekL[p@#-:M$w kq( EPEPEPErOq9SеHEO\X+tZI^$s8NR/+>еH1i6EWO~%#^ҤUE>yr6O,ށX2`s5': }4:%vahՏβcN2溹oq1;v}:~uux"۝z=ТO=-I#-&XAzJ#k}t"?C:gVsȌe9} z`*FhZcY]; a]?x"qLA@YN(γ>w?TڃYi}_k,ַ+ylб8@nmd8_ ~cޗ:EjHO`xQ~m4b{KVF8-䎾Ҁ-gY]<s]z6:'_h so%{CRJ6<+WM=z?RQEQEQEQEQEdXaYaTU5dFil{Py?U|}K I{ir\bIC; ?C}t"|zk.dF A'޹jSݬVذjKh8ϻ8GmRE}.vN(γ>w?[,8d}t":+ ]<5F/:EN+ |DӿOKZ.maQeEp.e+k-]?":Ej>;m3WҬxjk <[_+`$?/<kpR\.E$nck4MF=_N.P8O#Af6jtP?*C-QEQE=z4ȋ*̏ tu<k2ZmMΥF\3m_OtP;tw 9v}>;UvAKw\e0.щ*1kb)E%( }V)n4?[@%TQEμ |>V?/O*ݮ7y1\6vʀW}3筆f_,Ϋei$}/O*3<?7g?s3Ƽ!xIyONRsKQT|=ouqvo18zv4O2*SSi[Ov=@՛21/x>ij}yCb409 uBMߔ"^IOJ78ԷP#g4{ ˖y3'K"s]^j4? + [qX}/+ [IO_@F\AX?b]`tѲG5-դҖFz\,o[+ [ LH,Mfayڣ&Ԁ:*1ߡ[W~dhdV$n?V ,JZJQEQEQEQEQEVPɥ^иɫ֓ek8Vb 8Uqgl>l?% P}86C>e7Iw$rZY]FvI/Y5nk8 0S$>/G!w+IY?de$>/@yVo!AgRcѴG!>SNhڴtf]KyΠ9cP 8q|R[ }pnu'!4SzXz4LHQEQ) Z(U+ NP̞Aa#L^3?%Ӵh^\jPgs +om_FpE W`{\}V-:C$im:I,;=;P# "]$ݰ/\Zizm;^^YN綑K|81 ].lz֫ekŨ+ 96mbvگ$x:զןh4;2*>V5W^_%wI /My[);׈S:U] x? ׃{{}KUqTE4eʒ3Mpm^{ƑyRב. RW];Rpk8筯 ƿ=m^Z2=k翴*3;޵cmq-YU,X8滉׸c0lתZsNrWMՓnOK9N-܂l"H9f#p(ǍCAVVHyL\qp* 붇٦U:ψ5K"+uM1l|1;=G={^iqrFGрea 4Um OǶHJjЕԋ`TA?ʽU_IRUir%2&c6S'ְܶ_jwrF ,mq*ۂ0zV²~pmҥrC\_qz7&& }a>rbqXj *Qo_7VBQЫ'A%~Gh?Ss}c-h~7]?a:u$xbHB\4 +Z'3M=$LUcVݵFz\X^"Z3@^P ?S[+ɭ̍"[ ʟλ0ӡ8i+~^Bn^VXU= V:}_ϵNIp6֖r$RLFMUq\2u:Kk[/ 7GO ^ߘ??5y~a)t3sOOe??E5t?8 k[/ 7GO ^ߘ?/?7y~a(ɹ2+ˀG}?|?jw# 0T0 ^ߘuYILCbǒz@tZϼ0ȍݮs̒yaǰ1Uu)8C4,IeA= 0i{M.U? ^ߘ?/?5y~a((S̼6Uڼcހ/bU? ߘ?/?7y~a((Q}6vZ"yA@1F*][>Kw]2DHYw u# )M%QEQEQEQEQEQEQES7fXsI/Pd=tU:f)ډZ6Ѳ M7gK}}։ҢfY[ 84 0lFzwthbTA 7tS&c*ryvȥ9C|>~ {^_N^$A5RΦy! łv{ևk X.5;0PF+:>Oci?IӶ*K_ 趭daQ;w4bo'掟}bx'/MAmZ%LR?ĨkX'4ͦZ.T:8Uf!G M۠룍_KVQ>W̢xu[i {4asLýD B|`+F7>/rV69a̒w>]?m~ڏWz߶϶|ϧ:AbsbBm#䷶JF]>t:qJ}^A(֍g ^b/e?G_&ZslHf6mAѼ?g}h4'OJ^Vt4 Ϟq8V9)lѬEOeqv9f8OsLԮm-eI+$ǭ%)0(((((((Ϫp@H$U=0+>*|ⶺ[`[wL^ⴭu}2KkMF{^(Fu\r(*Z5tn!S<3+,f>ȫi$rG>nxhBU#gd!!HSWzs W5wf%?/bh x ;W? W+=ѬK{++ $9'ǤӸȬZbڈYj1N?ԯK U(?|TW֚m5{H2̞Urz `gj?5?U:R'HdIO+T͓;\t~p%342Z7SӰ'/S+Q U//S+R.YC.FGWj;'Mߕoo%FڊD$Y#u !A(jfoׄ\|oe_,ktt30@#R]h'۝daҒՋ[Z[`U$zqU??€/L [|X.o.hîGCTn~'Rߤ ,|>nkk ndUG920 q[Z![?ҙ?~) MPZʖ/u0>?V[*X4魢Lss^|UѕU$"7( R) m~[X+éũEF*mMiQx{UiS[p.nn.yUv;]KIߤ wA[M-mG_?O 0Vʨߤ D[EQ|P mҿ?XXHEHUQG_?O |1ir|R[߭5@ 199Z.8~N'Rߤ geoeeo Ig϶cuTG5҈bXPIbrX(ݏ)Jndg)Ps'?Fdg)Ԣ? 3'?LgRFSv=0SG}%.dg(̟O'Ͻ[O'ŒPrz%'<)" h̟O}#OwK.HNt}2q?^>n/Q{? o7@ ? 3'?I?g-t}2q?pI2~A* Ƞáph̟O}OwK&Hi|G}%.dg(̟O'Ͻ[O'ŒRyzKn/Q{? o7@ ? 3'?I?g-7B6pJ>83sP'?Fdg)&jh11rt}2q?^>n/Q{? o7@ ? 3'?I?g-~/mn@,~.dg(̟OȳD.p8=E67KZ %wK.q4Q? O/Q{? o7GϽ̟O}t/-mlG\ }2q?A* Ƞápi6#*]ɰ:vdg(̟O'Ͻ[O'ŒRyzKc+^Qy\1n+ĻM2KM<_K51qbrz#t :rHP36qySMb؝>DO8 t}Hc}rlD;XaFFr*cjLNb;Fmx^Fcs2 SOA4ƲQKɡiZBb9E6}-K:M-4'-V|gR*!Xy6Ǥ[0d]m/9}@U?_ żpBa?c~a6nu7U^1E%2y],K5MoskRv # EjLJZ_ܴTCmU0.*J#n_.:A46Ѧvo@$l jRe2vnӘBݖPG"*?O3ͽK laH].$Mt iaV5-;ʍpI$6}rm/D(#Vb:ԑBMKYh$}V&hV ;d~N59aD>y#2ri1Xs PHbcBh<9AbڍXZhj܀w6 ]/m^_Qv~:nu-$c |zڻ+,s*L*zW0 6Zew_ٳ4ֲ$˾=)qErIOFbXHRl SP}[_ eoAwu"=('d WOrv7a# .ss;@- R5 NrMN K:5)UGLSu6ӵ [ZHv-NH+OAu!nDV$K7ӥY'|M6d"Of H(jo^K7>=k?jSBMHw(\c#5h J9vXʱ4𽥼zƟk%rTģdy&u ZiC2\89˞p(P6Ajsim7Id 0dC30>ʼnsXŪn}6N2I ¥us|oZ^ 4iZs >`1P:=PjOEڔ-1}%8%9)Ot Y5-Eねul0B]>hkynuI{V͸V n{J/, <W<147-(U&Uh5`s8ȭPM&kJu/z$~*m0.*J#bҮ-.c4RC;ԻA2<7L]cVƛ-H-K׌%ӑf^$r7fY**O'֖:̺|5tq#pmMUᾍ?iԙn 0B@%hӣ;;i,%'I|@Ҡҵ{ˢ-d%^io^jEyGVWYQզg1^I240m#^l:=Γyy={ct?TubMmK->ƾkMŲp{V q~E?4ݿPE(Š(RQ@&m%+,r7hfx}vgUERʶ?GUEQɴ?Ru+UF ( ֳJҲȒ7hfx#5UERʶ?GUERʶ?GUEQɴ?|H?%ܮ j*ƊTQ(u򴬲$)^2~HGm=o:.Q@Om=o:.@Om=o:.@OӓKWWeVSO$PE.jlv l72Fzm=o:.*zt]](e[[l\(|0]ݝĚE2"1΁dڎzofCP/ote[[P/ot6Qnu?PAPQB `8M+JVDC3OitPotVK*zt]'MA.u?PXWcEH*(P0ֳҲȒ7hx#5jm=o:.*zt]])e[[*zt]](eZ=b?"nbE,qEQEfotoxx-15.11.1/data/images/adjust RGB.jpg0000644000175000017500000003353112616075370016430 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|tonemap| 4http://ns.adobe.com/xap/1.0/ 160 502 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? KhZ%5I1.3:[[ÓD@M|Z\ w*Κ}ntOEJǠhןqSlݴYD#_ M3?rHX|{܉dXғʻM24=V{)ls1?j[jD#_𨄳D|\P^\,Vo6V)tEk$'ןqN=A~+67kĊTB\> րmɷSE-x"\_kRҚ'{*mcsecK Fۆ\?ZW ٷgp~oOy|WajPPxu@IcV?;.Ymtź~Tg3i~k>ߵ OKwk̢קn?Jþk>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7k>ߵ >k>ߵ fz7=^(lV)##;FE.g *[Ҹ#?V,-u$bQG^B^¤t~T8)+1n.a3K$>+G!ܬ޸WGH8м@fAZ#ITw/`:swqMo>|"v0l~T׹+W5PY#=X*$$!3ҝZ3/48Գ(I[-oBdz>X.R2l4zu ?ZE[t(RTǝcnzҴWu Xd .\fI3W=p{R)$y]񹂀[2{֎ڿ U,6X}qRy֯_ʓ Q`Gy֯_ʍQ`Cy֯_ʍQ`Cy֯_ʍQ`Cy֯_ʣ@!2ڠt)X hi$˪Klгgn8ԻWCy֯_ʍS\Z :l=BUdOТf<KKQZǔkDAܴ -$96]G?r ]%ܑʬ-H[y9XGygkO ,6nR@t/z=^mn{ Y;vs]MuFѸʸ*FzCIX;գjs$ybR?|Th<5&7Cq^8uviRv$u 򼍁,Iڴ2i18+öve[1!y O%n%R! 7qs^ѸĕZ6Yn۠x-đm'az,1I,#qe+Aqq,%QHdw"sk0eY7Xpg`xQV*%n9:'re??>_5QQ쿼Ou}'_C5;]e{/?>|kAv&%ՠk3%1 [5ܠIYN P{STv'[Y/DAi2J_+AXTg՘-Rݙ<ޒC?ڦȡ}G~?Mg)}G~?Mg( 5j.6o93|qR@Q@ ~.^{5HeT'9Q=rPf[3m =85d7'גkϵ_ʐG qFsI^)$h[`?ׁA]F@QmBLP\7]DҼࠍT'=VR֌:M_km$!SPq^FL,r^5H-",BN0Mtv3 D+q YɤV >I_CyG?/!_C=XO5;G|kAv&(_x}c2t%j ._iCA:}K{n:+mې1=Ŵw!wnWCt8e>Ƌ{hm/#z++Iܫ}G~?Mg(C>ϨP??AiE?s~>WW PJf ;Š~ y?[r_*Z<KKV@'fZxI%ٝ6l"u>ilF?ZԪvdڝ-p \=P {;mO$_&\p=RźEČIM,I}*--Er=g}1x]dPKA ~A~dh@25?#GG#GQ/ @24_@ߙ?#|o#|o_O&Xv:%( I)??tvi.fbhՉb;Ҫ>{-=`BO$rN߉?h:;o%wg@Χ4?j`EiğMvޗIԿfopm4EiğMvޗIԿfopm4M \!hp?CIh?z"FE QERM%@[4m|֘rfS=Zs^4 $&K֪x^;|oqܤSiZ$l[@ύZxj~"Gw0K!i*[oBVEoglxa@TnLAQ\CineMW0<3$[ƭ}L@~?G%X;?c/g?/? ?̟MO&wH!hwH!h%??CagmpAU9ϡg>xT\%9 V^D% T{8XY"U=I0/4iz_'RƏA}"/;OK~$hӴO&4?fo?/N~0?Tfopm4WV%&6ܬ(SFUB(GbͧyA\ Y&*u3xwXQOLg_BN:3mWRG`4?ѦRM7KTr}f@9'9?Zs]o/%{_3paPL)#'8=)(yίB`1RH#`c<⤺קW<::Iw̹c C;Lgny)>>o=גCದHh$~Uj6Zľ0(Υme؄o*JoGa_Kڲvl◚nkuE 3mo>԰Zĺ~n/-3\$g%Ճ>31F+YAfsY9*KA!5p:M. RIC̠1Pݷp݌<ъb.|cw7q6&5ߩNg8IkQRIvmgBrʲ3(S׌v+ϟYͷV_r/FaO^WErqTcߏT{__y\]2Iv0Er31z@_7/*Ye2]cߏQ~?G_y<_bE$Lp>BJYSБM>.B.(Dyn3oqq )H1ja^ [+Ք*31`ޔ]^óh>UK8PEW#88^ԟv_-EUӱoN{€-TWܠW*r N{;? TB,Hz{TWN{;? EUӱoN{€-QU[(ӱo TU_;? ) D8$P袊)r{RT7&O ȉۜg8OF\u-/;XtBjBkZY e$gjZb6Z2k"UYdݵbbAi (xmycYIEdhJ r3Z@ ?+?+Žxg>֢+?+J =ϳ4n-Ri%D9Iᖋ{T~^G9f7zַFX" 7Dr9`~u$C#7b#SީjCVve*v?-jc=GZiq|P5;#-@_ )5wI4I6 (HkP%Bmw mTTg$hڔ"F R0> [ j)yk~,ZmdIzV?h?sfoe14Xr8 [v~7T2fotoxx-15.11.1/data/images/sharpen4.jpg0000644000175000017500000004516412616075370016274 0ustar micomicoJFIF>ExifMM*JR(iZ02300100Fotoxx:mashup| Fotoxx:mashup|trim_rotate|paste| Fotoxx:mashup|trim_rotate|paste|blur| Fotoxx:resize| Fotoxx:resize| Fotoxx:retouch_combo| Fotoxx:resize| http://ns.adobe.com/xap/1.0/ 8 506 982 2 2 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?=*'<ԢW; N1*m r@vsFrFAs#9L)E戔wN `w>ZE\iQRcxLW'+ҡ3Lw/5I.8Gr 攰UY{T@F*R`SyfR&Ru5'O\c9RsCv@IߜR(f(*3~|Zb# ƜZ5BU\My4Ua\wu]Veau(;Jѻӕپeҥ3TIրݍg%̜6GP @_b4w5Hd?F/ gc:4X.X߃Nަ 7$CREr w/msT|NZKEbݩFqޘ`"{+f=h,;AWbn^2TRKg;`iGOaiR-5tьjS !aT8 5adaOɃiDVS2tRWTiSe&iR)@[)+R6e63IdSgN~OOs 8;u }b$iT }iM=v!Fcp) h g5 C T9 *CUgyI*ѪZ6(rڳ[*EXIY>gJ'.pz%0ipd`7giǵ;o>72ҦKVT 'ć8W7mFF}(7. Y+[ǖI5!Y1Krhy>Ҧ[X)/jIE^;V^3YQ8R1WSr')Up秖s8خ_\z洗C;EnTZaYI"P֤2r4KRI"Y!|тJ%=9o~+>2ٕLt4 RFb)\+-`O4L Hy"֦5hyP *6V5$zMk;fᣑZ~+z c8偎̕j•ƪޢIhOZ֞; UP3XANRxn=c"js =&WZ %]Hv-3Mi ei#ve< E`c==jGMzUi ǵJ%noY vZ<̒I@6Kq3-WisK[Ո`92f"Q JkU~f@\#!|KÍkdڨ_zY.sދ̝uEV6zFH5c(!q&ƑֈG )4#ksF* ,yE/;] rmboAQ+EUp* r9 Z~R 89KLFʐb(25KsVJ҅" ł*P=)q֚>RC1ZK F#,jQoAVHW\2_֋:!TR]B;K| `$Ұ+BxS):05Nm=O*r=ۼM$S*oȨ%OJˊHzzVE,{;TajyT\E#G"@Fa濊-ϧz]iFV$ħEndLqL,lO,Jnmrǽal. T6 X )/Ts"5K\F4 1ʧ:,hIX$_Ԧ(,qsFkg& I 0!XmSZjHt_Ca:闑NVk 5b+ΟNG~LжQ6.D&\w@|Q֭ M QY'=Ѱnኸ4LRaҨ6H֤1jXiaWsj7lQ!Ivu)拻I$DO0 I/FsTYXqVlĸ֪ȖA5˂ST H3Y``j,1h9""7 YZ WAk&H-F|RRGİap#޳Y/" -Z-RѼ*7jv a, ,3'\s+W`e q",I[VL#յ9e\gWMk"s3k]n99㚰Þ;W?94 C)wFv75%-я*/j,Q:w.CtMM2UI_JWqszX+RMu ON.jdW{U9IlǜGW|@FBC'>_QWd`h.]# hȀsW-bS[]zfQ`6Bxnүj:) V3\o&ɑ;#5"C iI b)*71`TĻ Ku23-p 46R6q]׈9ЭMpvlZgMiOfss99WoO Pldg4e1+{/'1*;dUx}Wzb麷@ _\#⣎ܹnB UdL^+?sdɭb+CY2G6 iecȫFgڌI[0\0!O֟޾Y1ӴB-uAJ%Iܖ]{,**f$Î Gi  U(-iʍ,ǭ_H!n֗+6 Q,ּ Ypnϱ|=9l"n>*\lB'75~D"M5 ҡcL-WR< J.* 5rڔmiFڋn 1nA\0k6I (ȭ`Jky.0jD8&rrERAteQHXR)SEk3*W5Eʃ'$R1EkiHLI|Y>Rj0`VUcO`^Z\Y&Ak6[XA5+@=rIЦ) l_mOl Wk[MmOg-',wd瞂:j#Rh  |֔$Gw)3DQĞf AVY08)ۊrE$\.qWdtXnQՇ,R 鄡Oι2[3rjZ:w9XfnzqW ei9"Z!Tf5<^ȞmaĤ1Ҫ_A!{R>;4s$g3n Jb&eTzֲ_fV˔iQ#i7;i2JGrH n`TmӑZpЊe޽9XmEne;h>O)~s4㕢 rIQ߃]>͞G.⫼c5׵aSv zMm;aEoK̟>ɍU8/x]l>+̸O,ƓpNH:m"ԧ v!d.5 M?n=iJmFTL62;c4zyfƆ$`7=)&6$!j7`ejlI@zSojOHtKV xl86Wa3 7s?ιxJo<8*)T?[ڟݬp)(s+8fcɻ|gWÖ3O,+xd$GG$2܁M]xFՉK|aH<%k}OT)'xfBG7n=)Z&\5NH~'-zx"D-q{ TFeUmt3=rm GÁ[!HxRO1I;r9&;3XE#^1J:zR5-KiWjc|rpiB0Lk^*f~ Zu:S6\wRD>^sS>aQ OqRB͌HP& J2]j|JX( o^sZh]٦qmQ݋vÎ)Z^T}zl#uLc5E"U˒q H+R!!t/byVg3o[KIc- bXgӚQ.ԮQL],ALMXc[OG?Qtg?iwjew,^S[vvNFr;Vw5B!X7#qkNM4eے|Vu#̬!.Wvq8]_:yKTƱO]L͆wz'<;=Ɵ}WcI]yӼ4z̟o{Uk/ٝ3ÄOiI?f]=RoIS-S=?l4Qm']ODS}UcH7J*]`|6!?IcU} :-ḕz|F"MrC.Kϵu֢|Qln?ZOK▭" Ihb14U?r 5O&4dKֆ! ېMtںC}?t+3/He$TMzl']lmsmqD>?ئ%ޖ.n ]B3I? L СɺKc.{ ^]H.7`sǠR"'C} .zZI[%KtWE||SêH] mT煑PO#0뿵,?Qa?b` j@{`Ws{5LP`&?T:oswL9U8)3҂9 3yTƆ6Tv5>/cO Ƒ-ƛX{e`P:[Ju?'Z/?K>*t80xnh~?Ƨ۟]J~VOƭA G&0}8)ȃi`Bq,qls֚l6qGe2A]Z. ҥaL0+ Oˎxs޷DkH^Oɀ[VtlnM:WTm*G7eF<ă9p{crhc 8,+'w$AϷvںC}?t]GJȦᯮ8ILah|ИZ0F >ic>WIGqs(;$?UbtcPzY?ҋ?k>G]EAM(q]<:;ÝOhOqA"W_-΂TƏd]7M WdO^?UҼ4$OƟ}.Q:fsz4äxi2'4{7jp3Ҙ+ҍ=C?MS}qȨ3V k]j8eƜ4hn3TRrڥJfbFfFZye4c9SMi7C}+-؍mӰѦ* I>*p#ϸqqS3+'Bhm֥Sq Mˤ29W۽so*Wy.cHM"`zHڳv|l?LǪR 8F`Uˡ lK 6STXb *6Js\>ra\ d0SROqYCRr3ލB柛6:pAyJw4KcUX`cbvHY+FŇJ 63T*L;Rh(`lA] å07T%7J`ٹt^!7?A5q٘9JjZPS9iIr{ v% To(S*YUcQ1N?JV : w[+0+igN@㹢jVnVz^߼ O~4,/WvQ97)EJҪ$Hy2a_$y ԆTګRnxLd&< PUܤޤ=*cRJ#U`<~QI'k>䣐GA%è!EV^OC`zy]MfM o!aYTjT,YKC oIiXe+<(fs ƒnP7YqPuHS0<?$9<8GfYr\4qɹQrQ\t84mT 5 $ 7p:Gjs 3b{#;Pe04H'4op0HxS\E5P'$gSWar#dT5]T1q"n\ݕ anU,=3Sl y1;3Y Mspp}ꁼyxJ#gcƓ4Hor_dvոe ҤEO# fp kA5kO֐٘'6O@QqN2bd$lEY\'ޙ6%sc# U9ޢG '#␜ @Re"R$X'+x[|ئ}@q`Br `I$}k5~-.4lhsUi"I[b*E#&cۜaWYMFx뚛ı OVW%='媭nќbǡ$nnm\1Y8`1ڦV"3*f*G:zM)`(rϜq]5s2G"ݐy5Zt1T9c%X?STQHH1=Ǟ(*M}VA}*vaYXTqI\|١\~ArZ^y4\ijGpZFZChGsdbpii>"\64!nѶW+S$kQyi1.b+&FMBbei T;zVBVRo4W_ ${ iOfaYY88W%JvڦRzU2̵;INHXNC-FG&fjOHAI-NAB;Sʱf@h4{N[JLF9Ȧ;Qf%Ȫ ? V."pRˍ1sPy3Lޭe$jX⤢!IUv'5N""F#v2EgϩEm>N[ۻzVpue6C˭cLI5|{Ӱ7m׀2P.MfGiڸQW/Dk0Mi$%*²V)Y$$H85E&qZ0_1,+X[cojEXL8ɬ E"SyVTrOj yc cfWMd|C5c5qvW;ҩsW*x4+,a]sxMcUzV.6eaV38%a#讥 ,UYB FE*\yP$F7T8Tc'! ҌWyȿk&hw]ֿ _A5=_˓U5o)'BLq"V ׭;giD)Zf$ N p5&3GsP57!qOQi^j?@?dƃ.}MCes*aXkqտ*>Ƴp~kVK Vj*7PH5Jk3~5EY9", aneF Qdje'X[l'NI"K1)2t銩=үV6ʠWdKGwqvbg>t=1]Ū;W,aTdkDyIı{n%Uo.=kO.F6qufeXo8:lozTB&)ZͣxThauSUW ,o}(p}6tQ4n*Tkh2YqJNܵn1/JֲrR=Dfktc r}+yt8c#j*U_٪db4(Pk.vpBEĽgo*6j<0$媘t4l峜+4qUK6q[}%C0 KmV1h弼Cn{⯴|*D[85G;,t@v]&%q͍T1V(<`:iH'#kZKGM T+6K4\G&8>mm泹MXȟ;4nM:qEs)_w ҞDO~Njbj&4ݲ~m¡oi:ƤE!Һǖc:0zz_ !I<BFZ(x_ -6Ң6q޶llXw'Y.J皀ٜ]o X3ET x^L3emz& 22+Rk;;N iw HK(Z"'"t̷8T4tSP` 8)aUZIm"pKLH* ۞Օ"bFM;r{VlV4"}kbR)Gy[ȩP/Zi5NVrH5BXr*YhhK19iُ0fE  *؁wBe\گ0ـۢ+XJ9+PF1 u &#RAgqVyCGW1 J(׊=:WMdFvҭݰCO3E$RZR2|UҎBޚPsN*i/ndb Z#+W?k 9qO;OACfϲH))L9hlcҪ$ns/8+4*Hr=Mj[ ;k.< 6+Ѝs[kyl(nJWUEZDf}:U3 fb'M.y75V@Z Tm"Pޕ3FL.g]tbAjRIM&`I jCЊab*ZooTs/dz+ )kMcݤ9&Jo+i]#`Zo!VBGcї*y:w!39c?ZM#tFhAPgOJ\T9V8mcyBK9PHs#y9ߕ]:dRT⥚܄0MW),5FO=* jU(#AZpT`SEH _vEt y(f0JK>+n32mٙw=WGgG,$JMdF(2Љ`=q{ 9{c 4#ϧ5.U#1Vcv-iXfb)9椓O7+k$G;6 Ii{)q\2MN Pi`Ҭ+4C$v<rm1sU9`jG^XN!pw_ZKMR{xXFsƦPqZ NkXAG€jEn4ֈg6A11Jɥv;rѓPI&):ΘԄT:Q䪨9&H\`ҋF0iC.Zق@Fj쁝ЍsB.#ytS ` Ϸ]@Aym(k I7s 5џ ?1F|'9#YHi,M]]ҶmJ]Wi~u'!K˱^=\&fC1|o X1(aX0@f;I-oaxpAVCR[)lBЌsң1 YKqoEM.BR)1KSEF8NGq2)ƢdAImSB>j)-≯Hj@u4KoA5F@vڜ\h ;$`ְZ3LJa_E>dHj7A*^n1>:6E/g!9riW]p#k)GkrNQO8VtōyU1bN3 :ܙB0 m8?Zn[Uő"d gFѱlDѠL<dvyr#PyAN\.+d|RaQg|֗`)RG94"=(Wojβ9Zj 2:_}L%b@W@7 #ƫnt^7!-X%mAGԝuQ2[pyF7gɫR->v 䏸=Mt69V&8PW?h))˜Č󚮖7󾥧&'?tDc$v^?*lo$i1@ۮf].{ {;xTYSWu1YlDQ;Z2 #l$u{Tѿ.[<+W;Z+QSGޮfub)PA榓U^Jc O)qP4EZ,HW%qh=U8UMR5 okTJAU|`N> B5[d&qW77dʐT0R=ldToct,Bo1AF}q]tZl'ɨausӟ0mA=+nh^ -㸲ԯ kxST05ɲs^{7Ϳy*:(!v;ttFJi&H8!"X^ sV$I=%@6qMZ!OvSK` ó+ejZ?V,GCE Gs.+# 9Ne[ l'?w+SI￲̆9kcswwfm+秥jg*= ܙ"dȌ6B]iiimAt-dUFa/ˀXe\*+[ 6YY$z7N=s@d$t$r3ڨ޽Ԟkr%Č],v|M Zl\>$qoƛZ߭qxR9晷-$}h 15=A7ZE"fjR Y5c՞j~YZ3fotoxx-15.11.1/data/images/user-options.jpg0000664000175000017500000007203512616075370017216 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:zonal-flatten|resize| 4http://ns.adobe.com/xap/1.0/ 409 471 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((k" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?B(mTTq$3pe' \4zl· +5mfO};L=Oqb1>ɤ磃}aJM# tVJ_<Dq1ڣ=6<\I aqw4[.~WMW龆LH۵PWRqTTqYhgXZET8 6 fjW |lê^2:EKmdv~_ʹ&\[NV,w;blpz}X}z/^Q&#obduXNcPqbk=֣Oy-ʜ[4^o_E޾WĚwޜv7oQP,tomqn.gsqʃ!1֋`޾Ryas3X֮4mc]ż2avEW L>3U>OIRerǩ?&/ȣɋy')r;W\, / 8<5K^fttbH`m$6qc\om>$Mw*ϱ\xwE!Ӡk8Ncdm>5)Ѵf&#B \JηYZw >-oM :HGIui7Kvhgooں_:}gGh&;cJmnN'ԓ֮yηY<Ngء:}gGh&;bGWζY4}mm_ηYO&;bGW$hNgءZ;u+V8$@ȑAi}hkgɋy'(bI||ϓOQ<ro66|"&/ȣ.cyy<y1$Esͣͭ&/ȣɋy'( mml1$EL_?G(\hkgɋy'(bI|9B7G[>L_?GOQ1Z)%Wm嶴 2dJvz^_zy u Tr &14d`ǰ>VklK y`xYvCݢҤՕmUm qRL|iua=h  $Yf/z$gO\n*u=:{˸[0\sQK7YM *Mb-˅ YF)F7TH ^B0:3`DJv"x&?/-%x\."\~}`*Ƥҡ..4׎*+_Kܥ-QEQEQEQEQEQEQEQEQEQEQEQEQQyI#^aOKm4-}>IPGiws]$`O( ݀O}>/֠ (^ϼ_Dr#G"\52 '`[$qտ3~us>Ӣ͏*ZR}'R6|q{α]hsOw|͟3~ty~uR}'GwKM`;l~ty~uR}'GwKM`; Q]BB&1;IY~Aٿ.i4fԹIvsf6oi tPjǘ}GXٿ.i4fԹIl~ty~uR}'GoKM`; S{KQ7oKMٿ.i4{݃&$`"(YҘIn+$}A=k>;'GaЫ#~@$4$v&║EP?#&C&?Sl?EP?#&/Tջ'Ahk1Ed^1: qahy uHpw ]Oh+s1F-ȩȻrA{HfO+){h rOGyx~E[+ȅ<0xGAޛSqS-&?"8E88NyinQ[]gw.$gj%\rçE%䈹8 ?J|swvczD8 J4zZjkLmRM9Ψ?k\74wr)p+-RϤ#B4iV1QW&7kO(K1@bT(K1@bT(K1@bT(K1@bT(K1@bT(K1@bT(K1@bT(K1@bT(K1@bT(K1@bT+.q}mBZe]N>-MERU;7i[{QGs sDE1dLPY+G/ߕv0]%9$apFGK@d~V^+ZNUA_Zڙ>UMLh?+V--|wyY_fB3!iL/-d|Oq\v{|6))1#Sw?g}$d nn! 3w?Wj~"hzlsZ!v 1DDmr?ħ!9rEuTץi8>n! 3w?R}p-̱w s*JBB((!O<&%UvsOLjBBXwi,kECf@A隱qq F[c0@#'d.n! 3w?Sf~7>fn! 3w?S/.x>URxnj6wl, d*=hn! 3w?S8g}L&(X:@ BB((!I504K4ҶðۮRP3w?QBE37)\V[͕Y$8:=)g 16fhƟD'OB`U/KXqgfO9=g*mvh۶ 8[ :bz`dE})mA??۶ϒ_ʘaO=~E oQvSQ ooQ/ݷlƵf0"ڟ oYQOm~nIڤ0KXjfXKK#d*iڬH[!Fn! }37ئWvVoATPEP^yukR42N-dy>Q_.z9+.}3Rk+X. VTVb#!y$n,i;[XEYcۉ#gszmӰX/dԣu"da$Hh 8@*׌ߋSX "d(B1 |h^x[W[؜ݶ1Bsy76$7%PQ5Z#)0Y0pjaX|dԦY;n! ؔ=6_h:m$H}HίCXWs|Zj;دƞI]GXweQ/.bdl}@+5i:uRKq֯gnZĐĽxIFQ;l3cJc5\RRG x7d fM^w<{q !*Kݼ3~'f+8Ln|[wPyɮwuu$w?ĸ%%Hg!י^ŭiTQׯ_í`kIRK!5,p%BNP&4I@z+S:ycVC =z_Gìv VRhJޤqTPz庶[G$s\Ȏ*uhuo4\4^`NށykѨp_]x=U4EȒ|wkz|sou-3KH#w(s>c:s]mtu]A-lL(.բONcs*zij[I5Lv&2gzFNԭ4p2,|ȦxG6%E<#͝K ִQC$AK“A5eqf%͎A|1h_yIxF:g >W?] >W?]UbڌB?|a5[W8 GޏC)xEՍ/t/vl|L΢iwRK{ Q6Sw s_+?.+?.'xOS4GO{{MB}:V X`:Kmt+?.<ׅ=KW5HWbU[sz-wĮ_Bo⪽"F\45QHaEPEPE_#xr[{fD%,IQs}j$aO4dy;{Eq*4kI.8̎' V^ݾo~{' BW= 4 joQ^ukRLҒ6UFΝ*y |[\¯cmh3,c*z\>#1Ӎ2).ПNnR+y$hIHe7[Ll=:w(7XFݒM=X }G5s÷*I-l-zo _ש\-;) k#peP۳cOVd ͕o&ZVp$\ׁ@f[Aq 'k{*$X$.;i$RlV0I 1dhiۓBizj??¹5oe޳ ̀A#UM:A6%1o݁&O}ÑO+v A  A ak,AnZ˓gp8=jM=okm,eV0ʦqg/SQV:J$ۙ?Mmi,zO?dIE}3;qht_!!}}!!}}:}|6mc-&;W!Ac@ 7EEaf8>IqQQWNDLpoy%b?ytc?Z|w2(WV=psAkO}A`X틥Ħhd#s]+'QE ( ( ( ( ( ɟÚEdNCЕOJ֢3=6GFۗ  c'$ıYHѮr~V9`s>Eb?tn-Pp:<J_ xzA)$|!cV>cog%{~"PH SGBJnYJEgǢuIP0^p=j3in"q}ۛ83( |kab۠# Fzқv=C9<jѢ2Gtf&XTvf' uԚ4-9fp ?)ŲaNa}}_ etXe 8V9YJS؁S^hz=sʹ.&NqdJg% υPG1贋1~#8${;ㅧ! `LLT~/X_`|,?€'4J,r~3VzQZl L]3+Rx8l;υQ ({C9ۘLi?zKghi[D)҄b$WvI8$R}_ >/X_ž[n =sXkk73umɞBĨ?υSZ!d66 Xg Oe:֖Eu !g':֗KoJ㱧Gd_Vg-?[ϴ ~d_R4(H{߲[ϴ("}\V#+$!I6U@U*kiDYX _p5jXo :Ƞ 袊*:˾rvO^j\E. -Kk.CD օ=g *1<04Kӭ%̠4l#We:ae{I#s@Eg ~uWUm4x网9F\O#3*O^mg_jz5͝^\I5R{[Ims" cAi'Ek--*ceoB*5p"=ɮ#Ğ+QoY=S{ʒbp {Gj't&WԒ-2!u1 >-AƧ:IHfl#P@U4AU=mnK{ij$$%)N4I'48"HLKmDCo$>2F3v;>/qw׶$te@ uȟ ǥMi65[h,N2 7Bԭ&6~T[q*$##I"kr]n82) MB0Dz6ΞPTB x֒6Rkvb[%m§c`xGKx)#($ʟKѾy􀱾_ Uz"~li#״5Q,.eUh]U mb=5>?/-+5Ƨ]kgdu27ϼcoOcvѶ"2~Fy_P}}=ܷ3cs{VäiXI,b=x27ۜcg7oWKyo$i @:b/o hz(4C@ EPEPEPEPEPY5ԎJGhT+iKT^@Ƨt/GݻIxUnWME4n@o ?EGn_&8?w5'>G>@iۿN$M<Ϫ m R,ݻI?KԞn@o ?E#OӭGn_&8?w5'>G>@iۿceKܩ,̎Prqh3N]$M{^4Qw =$N((((((kD ǾhMoq,[ q#=۵MQ ?_I? lkElkE! q—lkE6;R>oԟNdQ¢ȓY 2@K˿Q˿QE.Čfgzw.E}} rWbg 3MEQE:5MPX"nݪ[Zu6{w^֬Qõas(njq\iѬsqH"ÍnqךӇIJǣK6[]y ظ$:iTBnd{wX@fzAk)6s-q&,LN* iU֣p5-:+12Fуvw2[ıےB )Në= *Bc9cYSxJGh(VGo*q,7H֐v~#kFn0FGW/v؝Qռ‡8RL݊>v +rak{Y< ƮnkȱM0tۆq  _]TČE GV!A {a-G`¾6"DJ+-=$``Ygi·[ m⺉1`Lwk?:Kr ﰳȗl 9I`kln9[hfx"Wߗ&F jI<%<"MF[$Tdb)p3+YA IA{Eb3@y-5ҧ$pI܀iPy$vw˺Ns=scC]{"P:Tsb6=qں{<#@E6simO=»6a @ y灏ʳ=mBZ..d-b4bCV2p.:5賋bPy7'G=(Mլ7*SȩlR hEsMkJRߧ*iz*ڶkd%$Fg&uzuEy_\60ϦE!OMbVuR8&W`$~u8GiXYdP=ir]0ZǍUrp2O? K5]J]GMFA&<8Zc&(e=x:Gu[ wwqnmhK_\^ɚys]1KIJeH@$|ʹN?*}7u?bM.0229µM\8GѕF3O^ g?u^@?g KG$m/;H'A=#-̳HJ\}p;vx#˾w>+K?t-׍%+Ciߌg>KXz4ƙm5_N}`P2.dnC\8*0m';+$(Icr:#<O5 Ah_'OZv[h#vC<2p@cN*Ӏ SU1Ka<7"S y?o y?oùnt+9G*O`'4tUOƙ.boZ޿Z* MMNrSh\EejziiychIRkV ( Go4@(@N5EfԿѻR+_ o7ZTPnK|)/uEfԿYC*4ܲduj((((ZW"ƿvo*M9V=PNyYZܺʹ:֒0}3Br6>$٧0Ѹ"nnRzizΣtn"mшUvJW1$cWt,Eɸ _,nͼ*F:`Q^v*u(}K:񖹪YYiɛv\p&GQ\d]=ĉ$":+'6AEռ39D WSԧQ[<gO mO$㊗sMx_ (|qKv Y3`'J3>#C#x^hn 0pO\V'_[ս& !M9#yc޻/c)42Qs4fwrmgt4m܂'hk5g_N~ӱ盛%hH!?o+'ğoi,~o;A_MvUYy~6{E}RJǡvgx[/ʿ/󨿴/6dUqs@ Ʀ4?ͯ6 ,gyoJA=]&+cv9zoE}Tq^i Rۡ\ }M3^)Ou+nZټr gMOIJ( ( ( ( ( ( ( ( (xMp<͟.sO$>?}njuZT6R[}D7Μ%(ժ=%$LQ95iR -PEyYF/5yʎ-=6G~8VZxR$nlgڹJN7K׍QJ7vש;mw dSeou8t[SR{DȄ\sZI2-ZA%y`~_yw;GeD,Tdp8:ذ򼶺f|+J_S_W]?/ţʰ.? OfƷHDT̡N9'hgUe 8POS^yu6W3;)cy!P@yJ;wwu=I$w*,dN_ZJwGP d.y"=jPkKn'gӞxpWWQD%`g1gVvv=FUE#zW1k_5K q}dfE!0$)^{R::V?1&Ѻp`z#Ԩ=׵t"} PIqMkZݵ{\D 8{еM/-4c]_a$GQȻӾ9Ϋ^iI|-5p= @Nq@7cGWF :dזti` ,QaUĬo19ۓt}'Vl<0@2 0~jsWV?&5Y, 1>Z9=kR_(RH8V|zM/jYQFX6e}uwq6rq8N_lzlA4>:4p,BLbKo\mKZikwfk!rq@<=Cw;SM2Kǐ3ɮbQoK{{2Ė#2b2@q@c9;asoϭ>Kcd4K0q &T$}gZlVVJm Jy0 }OzHlm!Sk60>o_3MԬ+$u8jֹeo{en[Jv zHEPĒIPEagX,`dA\h]1o767fc?֤KxcC`OJg-7Yn~ Gp h]ۘQq xg0>́tt=NE9Y 2`L!?u >PzH08r?oҩ:ŵ 'ʐrw 1GG `}H4FvDUg9b2}ѝ|ÓEPEPEPEPEPEPEPEPEPEPEPEPpR-U%DP9Y 0EXke\²쐦{bG"Ȋ0}B}U`GhZ{ZL eM;H;xZz.e֮ւȒ_%Ya ~ *(a~_pi]iMM i{H uN}MIeɮC9^ 0@@=q?+_W>dӒ)-/,pAV5&S`?5]_ aiXGU Ѷdֺx??Qg wV-sY1xme;~sx??Qg ],3B׃x3 +?^y +?^y +;^ykDlp0Oָ;_KgpȂ8ff?Z:XgXR4j 6ڭ+V?D mR%`X~ }, r{y[npOShlf'9*/Pzt35 k 7*03^{y/! 52758eWQ"FT0GCbk0X| >pfIU#IgڂE"Qg]}?i:$U]Hbvڳ*FOm Y.DHmΣXǝCKz,.2Q =][Lq Uq\zhWvZ6sZYE?)x$NkšVvwNЦSː~PC7),NT$`J}kĶ56FbEv #<+ѵ)ma=2}p? |=>ZKstjIu+\\pq :tgAo"r:aTk;J[+vYbXآؾ1lՍOM 7pymccڸ cR,$%@9#<95/E,YQ.3MbCš ڞg ~qLQi#3$$q?P{iqcR(`TW9\Hm.-VT;q=VE ,⳴"S}io)G w8җF>MjZYMɺ"l  _tq,q!8g j}aJ@@r^9-ǰ{W1_Xo>k̯kur8]8F;n:˻{;9.eHw4Gjt.dw###jM坥0C( '(K]&6[hK(RmE.6[hRmG&6{hRmmG&6{hRmmG&6[k.Xk}Ͼl;5QHbJSI@Q@ʈY*I0pG' 袊()Ʋu*~T5kKMJYpHP8PȦ5mF7d'0ikkoo"`ic4`ib-}!>y>ETӿ4`i(][CHhQ!J9`(][CSNa}Na} kkoo"ϵ7U?t?t(][CSNa}Neϵ7G?4˰mn} Kϰϵ7G?MFI(Vw*M=4=12@dăϵϵ7G?:w:w[][C}}!ϰϰϵ7G?R\Q "(frsIM 1r\ϵ7G?:w:w[][C}}!ϰϰϵ7G?:w:w[][C}}!ϰϰϵ7G?:w:w[][C}}!ϰϰϵ7G?:w:w[][C}}!ϰϰϵTH\\2+*S4}CmصGA@4QE!k5Ź%$!>{o]`׿JsQ12hЇ-yX7`ko'Mw;JD5; _'Cr|VcM")ol9}uf=EWk * o09⟨=F]6`e`ف=}@fYL1WGWDC"F—A8izWgG×+mrrm8h 6t>%e 9Y3:Roa׈47+ǾI,ulG2\__$zTo/(PFMdӑsPv-ծabn#ıdEFVLE##1^j `uQ\乾kQa{4S(6 $dbfh~MJ[Fs4Ў[ӯ䶴YS6#|=%-Mr̚5-s-qu36U?ZPIE!4(((((((((((((((((((((( vI*o?(NWrzo9Z4Pw՗?V}HfsV-R#3gnOҦ(fotoxx-15.11.1/data/images/zonal-flatten1.jpg0000664000175000017500000003170012616075370017400 0ustar micomicoJFIF0ExifMM*bj(1ri%gnome-screenshot0230ґ01002015:03:26 17:55:08Fotoxx:resize|tonemap|NE http://ns.adobe.com/xap/1.0/ 179 300 0 C   %# , #&')*)-0-(0%()(C   ((((((((((((((((((((((((((((((((((((((((((((((((((( " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?GMxaF,}Il}Uk=:wObWU57u/XvdKVz8,'&E<{*Hn$$M!SG-d,#+YY`u3֔jX);tCE'V%vaYphi1TÒAybC6WP[q$Ӣ(t\q}+[Xu-(P o 4s Xf,ǕYVZ..PX oj2L}r-B|Otau! OƧ*ZJ{CexZqxdsz运00&:dg)}yugA-Çc0@Һ"(!Pooq L9K̿8=MžIL}*S2!wʹwnֻO"(!PMmJP}:/Y,2`T'8$^aWM=A_2޻A۟º"( 0Ǡ豈v*<_t nyeH$pU~ۜ{(p:7R4qiW1Z]4drϨ $K a􏴋p8 ok\bWXJ7%6=۞J."p<}ևgq Vk+: :TXf22;B`zD9"cMg[ j;PcxZVZ?^&QE}Y̐Omf97 qކ~ɭKM}[/ktO<~*j;Sh}р3WMoė^$ڞ}k{$M$H<N¹}[/i[ki7p?m⣹e[/h&A-;_ֆq%us גCu#W? |-U@ke c:dF'#_j-}kg4W^c+zрq֘oOL.dHgpV( =Ny2T-[ w'lDr;c½ ^\_>mng^}YfS8hui)M0(((Er|3jW67mm?Ep? s~ 5 1^(L}˻kSC9A<DO:{V~Ibteċcm?F𦋣jMahvb%RNv8Jۢ 7Q$.R*Eּ>qsi4"9c<1>U岵Km &"|!hF pJH3.I:UBӴ(.!ҭ <<{6n,I:UObbcoe2 ̰+o(:]6VPCKX_ٖm~ٖm~smGnD?α,?`{^ns8oanʜsPY䞕a>VSNB ۂ;0dR[Ogc`@2 l  }Q1ϯ"mhBIWV pw7NkrkltޠO vP=~延}fd_)83I/^彵YsɴnbrGe`h bIGLtk {[m]H_9ɮWU<0<?4Z߱V-t+3KH!>WuҚJ(\Q@ E-%9zןj&߇tM^mƦ!gߏHLנ'xZ֦+n=@ IOABP{hgh5[bTcm$V3`3;*;Rz/*;q)sVԒ z">bc3c“Iwq[xtXk.ve$lۿt[?]k}\DrNTmcɫi\uqv%da7qmauMBk.OҗO2LG >&O/-/''O1iG_ h_/4^e/|eI'8cH-0Vd*Yn]?W|x'GdҭZ;I<zVDm-f]B@P"3t vZaK?[%HFҦҿ/*Km.H~AZ6ܓmNw++¬}>k?+ni_ni_c6ܓ_I_h?~G?~V>k?aƀ+ni_ni_c6ܓ_I_hm//r!Oj<[eе 6OkhṐKmg8'k<1$'⿆N繴.K#,F0x p# u:2mWZޛ7H\4PJg1&B8~ vid. n"gx *A=Ghv(Z!y?,I!2Vbx3Z3[.e#A8٭lHQ\iz֝}w>yWXd;FCfn:p*i|sGD/Ć'ﲓ9֒`lA(/3\HJ#7BOG Ao6,1p͌ 1.uh\KO&MNQu]Hh7%POeBжM[мGS]iѴP?(6el֑s_]MyuQy5W{4(!s$"a %vyZw}c@O9漯[&=t6 &.3^ f >VXpm`TKŨAŔXư2V!hc0J:B3T Ri,RhRrdb1?+2Gm o*nD vǐ?ߠGG?MSt?ͿO]Tm3dFm߶fzo.:)hFm߶fzo.:)hFm߶fzo.:)hFm߶fzo.:)hFm߶fzo.:)hFm߶fzo.:-iNpEa5o=SZ:MiM c=' 񦀚C_bK7$vohx[Ӽ=h:$qwv=@$WPJ[[sˮkh}A l۸ҷMc=j Uɂ=CH?k_ł 84-י蚽%8٘pTyzko_hꨑ]^MxФ`9w u40*3qnz׃4/LvRcMhmy<,ck(M.{zͭR\&vS:ߌ4t+B}9ҭd>8dT:֩xVwdAkn̦$ܥUml+{0hކg^,l]2XO۵RC$up#?/^Koìl ~&k O8RJ7oz5VW!czy#AhRp ׄPԵ qa/*.`>Lv~Go\]Ǹo"24Wiu-kzDed@:ȇ#?1PCym=͜7=ݰS4*x 5Sa^XuY"7vXY3F*=k_Ě_x+KcnaH"I?cֵ ]::Vn JFI=ɩe3$?:]SS+mCQլc2PsNwcVǍKPLxdu@<;PտO\6$sV+ HY< 56f tO#Ey\~?~oKQ^o7߫.߇[t}g?yW?.oV]Y6* vOןYC#+rASE[$vNJW@h$aэF0 n64kx_zQM75m!@ooSIkC 6? zz2\FZSÑRm!FCฏMY\I,$O1j=(B%,OSJkC 6? (?kC kC 6? (?kC kC 6? (?kC kC 6? s*O[ %#ЃZ}VQYsI\R!F. yPm!KEM'ަ"kC =Ni<QEQEQEQEQEQEQEQEQERJrmI1 d>/~Vfqm#E/~V$1Y+pH!>$ĺ%Pj$bEbFp3@?G."¼LwsCq W\[ nd]@a*M/%jW&McK "MRJNQ _>붢k՗yw !C-\1sYW-55k6X䷓f6`@pG;SK[ \Ayyd  ?SL?;y(BQEQEQEQEQEQEQEQEQERZJQ@֢On reh `v'5T|>kk>Ҟb͏@I8[+;}:%|~b`?€)x/örk%oAGb[wJ "b ؓ%sw@}7S(ѿ*W7x~gl rO19pүil,䣎U2=FTQǢ1h=rmVl)ZO74*̨e(ʚΏM\ꋡًФeqg𭋛kk wo :,~OF>F^eEI#RU;&oH"VkH2XL?F>`?€(+öwVos6?``>-%ZGAp.̎H l9_:7O?2bAqΰCo!K;i1"tTP~hfotoxx-15.11.1/data/images/jpeg quality.jpg0000644000175000017500000012732412616075370017145 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, 336 510 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?DӼ=cG"ncՇ-c}}@֡NhaVb̮ qpE7-v"yBª. %麳75X~qVA+g8 =+:g{[6)]^z9Ŀ!"Lq݁+(1WVK KJI^(,ei6+ݹ·ZuLjuMCB aH~|cfϵǠF.i3*̃= \jڕEZQ ]OFR(͍,kMHbBR Tdտ_ɠY ,n/fUfa gC@运wq{]`KY.g9;c}^DUGx 8ߴ} ~B:=ZiZ#j-'5͢;6'oQ\x]?UVX?-.Go~n>Ю/QqC1}1`G-.5xm&Q`&֠|ǜ\v;o-Ǜ4)7>c}j]~UҤ0_FѤ)o 4m@c$.|W=Ch/enN2ۋ'q@x`rf?4Kh"|w q|?!G=*7q|?!G=*7p,~B0zUmoY`EA `[}|?!G=*7p,~B0zUmoY`EA F)"tFV*TEWF.x\Yv)8&)_(F'` d*y͌S]9p`M@u@pn?ZAKrRP]0 {Ku bB啀ZVVW-r"zޓq$̃ӭ^ҳҳ\*I=Ihi#6EWG2 5sJ~J~s*Jbze0L0*=8XVCoeRU="??:UsB}3EHE؆gmۼ!vgn1>åia$Ap: zVFE[RBaxѣ#iB=1UtHM&cT8wZ7Z,0LKSjuX9@KiUۅ%.gXx֍֋Ym.-k+imX^%(,pX`1YmXvģ^8ϵl>n>X.b1޽Yۥˌ4ݺҴ6O4= $0YճѸ`m-lr7E: ڙM7b-vpJI{߭m>n>X.d"s-v$c̑P|th]ȋ6y}h}h\km=ġmNIwB{-toVK WB ЩuL63[[ /uoak g|ªힹ dՏn>X.fy֏n>X.fy֏n>X.fy֏n>X.fy֏n>X.fy֏n>X.fy֏ J +_&, UFaU_i=Y̷2]JU3hf͉޹hzx鶶q$hNI`ޘ6 .cƒ8Vlj.8\a!IUX =ּj:hnĶQ[d#+VO<`ӵR_I3S4wH*@9E10,AOdiT+}jVKH$\~ M:d6}代c qGZҴ;;ŷډ׊$H =@Yag`5Vqoj ВCugBD~V1qK2CuYl.~vG|G /Q (0[IU ʋԌZ̚u T1Z< Rq[}a&[(lΉ,8.+M# ]'lK_xcP5wiggntN5f iBXմKHZI 0sɠx 6+j&`HI*H=Mo9$KvH0HOX7T4٭[s1Dف굇m-ˡk`NN@ {LM82bQ9$/) 9Ⲵ 'Qni&g(mQ̪>q<qb *HgA]-E,,B$v,WPbdgҹ ?0ߺA{=ĠLhx ENp?8n-ek-Vx$YBۻ@}牴[l& "_)$V* +70 ~SD`e0Fp(M'gS)/P~廊YdTx68G{v6LL~a5x/RtOe{<]+:e|?/lvitQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEGq !Ճ#qPuLwfGgzOSu_gy:3ʲB 4k4 u u5&@W~N?V? r5uh挒G N?V? r'T9P/4gߣ\yN~~~\ջ]N]YL&:9RAEGc(6I/mX`o?ʬ4Vg"zƑE >iҀ׼Ga˼16NNբm+fz[X)Xd]4Ck Mq4pĜT}I)+9BPvњ[hy"w=8i?twC}v']69$3PBnom8|ߟsV(0n98UQc@sc<gapr(% ʈdfUE,Njmһ[6sV=N,QG{i/x&-cyǵ5u 6{l%Rhz*+k(ėw0ۡ8 ,~"0ʲ0 QF@:UPU[\M H0@[^dd$H_Sӣ,qx5m"4/d6 ]is@ugs;N im^+F|n[#~@=MmZVYuy$['kѳPYv,+$#ƝcMˆd1>_͞@c*{[5gWV;xԊ3FiZ6iVĺa|2KC28U@Vټ2 x4QS FGjѓ@Gz~6X%['uJI#AYjMťϩ s-ouV(=3OZefK_*fhczs5fG*ԯ?՝㻀w~G\Uk2nVi-7;Q-N8c@TtG[ Dռ]K%̟fl|Z5J=M[Ԭ.5 B_%՝B"*de?w'עf#OY[[Yo!b{&M>++ۯ:mZiHPF ?-zes{[<6hE2$:`rȠwqҺ" F 8LҶ 2\ E8V#dR~$mo9ܮ~GDENII4xG}hjq[p qzuM]I{EgL![ r_c(ڽ4fpn<y<E%)YB܊m/WVj5G 9[,3uc#psn"(bJ1sןzzLLV#Yo76Tʋ܏ƖHc0!e$#8zXnv,3@8ӗQ-۸_'pg?JĿG6zK".t&m685QC'{G+BDR:WHӼGh^*i]ys2JZ,u\Y+{C_{o5 F01iC9m#Vg?*_X=juYNE]碑sHdr=]C;D {R\ ҈vCwoiu Khde(QjItlYG2 ;%yg BZmͅ[ŋX HqT-:O6׋v|F^GxcQxf%+girf$Cӱ|QM]終)CB,O$}DuxŲhaan.;c$J k7B YdZƬ^'F1KHnV F+]~(㕂ј%X=/cӬXP̎r@Q\yq;-Eb_~'bWm.u |TrBI=ڦӻxF-YYFZeޝ= [i b+OSeBX$,$ynsA$Qjy6g/ӂ{tfQSo %p>@R?+jYy2r@9iRf įOzUaTS:S-\!ɪi LP CilZp=AU&ãF%|Ru'gM+[k73͹vrA(n=_H=BBS4?ªE:|Z(Ϲ V;̿7?ۚoZ#{ͩ8%Gր24?0ns& 4Qb3YҐa& =җoLV[ҴA1@@,IK;(_65nA+Pp\tI@TUcIU [e:ժCƪjڥO޾ ]F2}z>0Ҿev?ڍ @ ?0@Tz(-TU`xmF6CuԜ2y=iZ|w, cY#<~4g4V-[k{k{廎՚F\I?h};|=vgvq@hFch`uI*c`֝-!EuݛcX{BŁ۷'?AN< uK1 F3(8]~m`֓ɦ"dK`ۦ,sLՋֺй}9el෈G#S3W8] 5Cl,bMAmͲ>^3٭=:.bd=nI@sؐ?h&fc3/ ^iwB-9by 'pTK kEb4iMm>hwc d2b=ԓ~8[ؓ8Tn[Rg!u-L}աnNB#bn[QQ_o9LV-/j|!n]zTS@I3ڭ@ݳM=wj?k(F0crXq?TݨկjV XxN,,Z6qpxZgc49Č>ukv?V r?m(%ialpS,I]IHft|=}ȯs Dg SPӦKŮ=ƌ̥2k9V4]?KkG.|PWS| }g9Yŕ1ΥǧQ@"cxHY^4e].KOm2<#'vs\b9<0exqAIG-t-ٻuK-e=VKC"Gg[ %q]F*uS(Σ? ;ʻi6ڕw.#n=* EЩS)s9Eo36Bh]dNU#Q[>FG-}ߟu?r/o͛[́=+ᮆ!}oCݹd ś9w] [NΣ? .&GKn*d# Us:.-dV)u1F)(b (=Fz5_QҴ F [(l?? M⸷Dl28̎~>aHdܖᮍ"6#fڽp疚VnH{X9N_+KH&^h~#io:Z鰪L(9+āF>54dS-XQԵ-nm%YP E\M+R]kX]>KߋD0w9޻L2hI{aCj"<ל(^Yڂg6sТ㣹>n*uԭDX@GCwZPD$Fsbn<#\N儱,2R~ ӳӭ%}Ojt\dwUFpi__'xuXKU–H| MbZ k˃Z1Kݒ$eP| @sڽ'$R>pe,~'Եx{ԩ;$O꿝].K #2C9L`eT  ֓qVHգӵFiuXa0a'3qZ^ ]RCf8n@7[* 種'֌H * ř|ybo>;Z8J!GPA6kI3Ɛ`2( Tf}NR+݆I:&v.co3`qx繯I'֐w\xӭ(Nn]ǖN8Eq_{mr48c2xj֍ր9/C4S*^mR]Q]]w _郏{mY4Q2phnyihA]rp@qD V z`h^a!@ʷ  VţZ=Id>mN$u(Jy6Bzmޱ6LPHcy 3c+7Ȩu7E[x - e@\V .SS&e)(\u1j][[I9RBMFeԪ$fYO;a¢cR\.-,8>OKLc@7->p1OC+uqa]'aq޷Gi湑v@^W8ߒ<#mkzRErl*>Y cK'Z\j:Hm69Y(f@v@ozs˿Kmhrm[&X2*98TzmhuTD:+c0 ;QXyUo-?!$I iǥ:+iMŬ3 e_h`dgD{Ƶcq(idP z(N9'RI閶3:Z‘+7RORַo:o 2U~?0 ҷrs*Lw>j/@Юu1n. Gd,`㯥[mR呤B/q$qU|C'4[2I+9\]RcG>= FY4~a$(R;pwam.^nH-VTIVkGC:T)m$n( 9KnPҖ"9VxER)lcjw1_Ag,|ޭ\O^ơb,xgHԯ%sDgsM =2(WomNU@JuK|\OPGja.0?#'vI I{8 ›S@hXRVRʛq^F-h!]øqƝ/{֯kv$VjZO ޡ'2x}zqHVv ̰gn.#;eGY,I*e>ƹk5Χ\K-Rq5ͬpw`vccAE :QE ( ( ( ( ( ( ( (?wZAӠ5ǒ%$GNN0OOQ@`xUӦ`vg|\$<EtGQ\&[V%I.kS]o;:[ ȨC.i 6,Q~ASC^[sgVԢ[;)>o7T1_j~,Lq ) m"#qci۠\j呋;c蠚UyһO|1C {.$  <SSLwM ~uxMK2FEldOր;&T xdbL#wּ$I,o%f2 ׵۹9>-6I|nF1ہ@T29TVc}x!" n;AAarCD.}N IR ARQ4$ϼKol@1 ьv/Kv\|Vr; 2K͔7 3^^G'P>FIRnʈ]*Ic)FAEyn}WQ: .&Z&٘r8thj-+Sokw?-Nװ☏AWG,ն6p} :Gէc祝ƭ!id'~.VP˒Fg6PqRAq {:ڗGWM= #0R>Ait8Z%tKjz.3H^?jKQJ1omJAҒbO>KGg^ac[}O2_)ˎ{"z(CweiɎhïjp)vJ&KUu>XOJ&I]:E`+BHán9>>m>V=:ۂGPqj/t?R/-%}L*I@};rS. $2@aٴ*ϦY9*L$t9#5oii@`U {+;Ynf8.P=?bַrDN| MȵSr}IŚs s2?>~$oJ7v UNUI o'hu8@Tdccx~m1ya%VpW`N=kdwM}o܁v Ğ[‚U jZitֶsy"y.Fdoq[ i j.,8;~N2$9$$COON!frMaOT,sXx.c`0BI;wdeN3Fhp QI х6. NңG +O1;w4 -]cّ BUIv틜8' b)sqX/-}m+j(&HvYv 9 s ʋdc 9 nR}qE-ױA(wɾ2J#, H#u=4nT_o5Hc@7+ 麽{x.{{ch@ % ;Xm"8c m>6vF@ QPhm>m>)O&@ E.;RPE.(}((m>m>)OO6]Ҍb( v[{tv3)+1.?d>/ IyujqZ[;y2y 2N2 VtKu,X-& 3@`_Qå Äf*}x5'78v71~5[:La[8L50uU^ ӿ甿Ə;yKjɽ#u/++^ma4Zk!Ot} n`_G<5zͪDcU?R?r^--Y]k`rTtMu@:w!t{-BL>5X"@ܟ>KVaxBTH1L8.HUJG[ \ӿ甿Ə;yKjɽX2߾2) %O]8L'Vqo3y6skMig&$Y]@KSCYC*HFAA3+xj'"ҒH\*z6dUd]"$lNR?t/X[=DlՑɷY;ɹI_.I̐&_]0f ԍ鈅$UQMÀY~(6W-$g$_!UN~kjZHAH'gn}3,Ҁ򌢛ˏaัH:LK{r-N.ͪJHN(M[]#H[k NͧX*B*KNrdz6./s;[m˜CuM:#Y.gWm8ɮo]Yk2z<כ*`^yPv,r6l57<5Gybna"t9&Biàӿ甿Ə;yKkOb3?t/NR ;yKhӿ甿ƴF(3NR?t//n︿>/n︿~(s}s}F(o?_]o?_]?bs}s}F(o?_]o?_]?bs}W3[k* : GV1F(Hi8| 366Ro?_]o?_]?b"{$FF. !U5mt<#6Y'^}_qt}_qtQgmGmO}_qt}_qtQgmGmO}_qt}_qtQgmGmO}_qt}_qtQgmGmO}_qt}_qtQgmGmO}_qto?_]I1@L^4(+ԟ`)=4 r&Ic82n H8:Q붓}4?r`SuonMSuonMSuo*^KV"[:-ޭF !Q@ iQv ҷ:UߴEG.YZt˹a@K,$LH$`@<TRenzQHbEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEPyFtJ(&w%,,H{V~ज़Hxm<˫yn32FFFx +j -x_5X=8l7G#2QӬ(i|bĖU|Uo Zckr:Ma=v=}zqvz1vzpF-%X7-~\!Py<󚲞ԖQ\c-~TTGPy8l7l7#;:U֋[n2GQG5`?Ml;g:i-b 15faZ(%G >l^5DF$P}*u."dz #r ؇QpIm$kտe!vP6PKmkO"3Q0q{RHOZ i¡FcVcV.!G&I&5k`APY ``zm֓jq]Mys:a|ĎƯVu)Q[h X?abըŤ#U :E!fotoxx-15.11.1/data/images/add-lines.jpg0000644000175000017500000005341412616075370016405 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|sharpen| 4http://ns.adobe.com/xap/1.0/ 332 454 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?qF2qɪ[#7W5KY!+zgp?y;0ojoc%@T 1\gP?\f4ru=З4`pd0w)Ϟ*F5)>Ք6xql쌾 UG)IitU;˛tMy%݅`'wY%0 )w?!YVŤ1)-Yf I1@?!F+-]Ny3OzY!ܻW'$j֨6rY^^եa,eݠG|%RHIGR~BRYcՑ.V)SI.Jo>Z8ϵkkzmVW>t%v|Vp:Pg FPeq\qw'.Oݵz^<27G;jYd;sh?!F+msXV{8aa_'i{~zb=s_ [!6ݻ6ހ;]OQ iw2]7K+II-W D V?*?Q`fh#6<'o˷ۏLUXm3 `aA+w\6Ms7rd%O{9 ֶ?Qʋ̫-o0[Es9)*Cjܭ PET#oj~ oE4G$6Lm=&I.-bIcdyG?*,3 @gs~l*R'jpL[Kxf1,ok~ 2ZL&'6ۓ>f:g;K9~T~s-?Mkh4XdHS$VGoJ#.~Qʋ̗,\t $2em2oӵk c}4W7k:/'w;!8"9 a%O#cXZE~7և?*?Q`ʏ~TX.g}h~ ZE~7և?*?Q`ʏ~TX.g}h~ ZE~W G?*, ((((?w'Ų_O $R7y3N1۷wRI/iwR{tvǦH|OYصsmƍIVDR~m5u]SJĚ-ocy61P~];OOl@@n>l=2hPgǑ*ƻsI'{-fuǕyEC9",k*T J϶ݤz0F٢2%A29ǓR*:>.4?Qi!XUo9fҪ[xUTT&E& hиI݌g`q޻o)ͼFXTRyctjq8=)ghm-.p"_0M0pgZض'eCl@1_vq't*կtKKI=Vp <0OS}akP{1<.p6sxYk8ݴnwoʅIܣvA>*tM4fy+$Ml,R(aKdcX1=j}^ c0qy~@%j3+ɧqjZNgv23Ls&b_x KC$E^-I;;K}ʋq)[5RnQTM&/}2ۉm'@uƭ=Cšn>gh-¹fx{oUn]CZ+KAGUrW$o#`@Wk@i(#*yaPjV}qc"A"nSnsL70^!{͎cid%/3;7t9Աؒј7#6 ֜vq GA~#7l}ӚOTo=~y+vݸ׶)0G5{mj}D,f<@;:8~VHGA`*Ƥ6m$E;ƒ 6B88 0Euus4reo3*uS44+bH4. # 8ƻG4!Ӭ+oV/F>_³O ŬjB8PЭ{;DdfP5 .$dyU2,FGlԕZO|;Tzj AEP0((((((((WT..|\:kBG ʿdpija#+8o j[%[7ϩ֨'5!{EzKroXJIA4P#Ѯ&E֠G>9_"]mi۟S^QOmouso0?CHw|X|hyް,ç m7mͽĢXRA`ףp`E6|7t=dZɦE$߅ F^hVHϘȱ_F]/OiNXeee~9P[C`x51ZbV{m^~ cWoEc̼+xaPtgg31N2=_F [&F[ hC'0\I!wSߵkXx*kuH%Ɖ{s7'8GWgomohr(>p2m?>eM(Gۏ0'9>5u!sGG!-F7%Vf9 k(-#Rs%=nmVQy. 8npj~ԯ5gR-=c}?@]Aϧ]z#> m6'H\v#+[Ioe6i -%+wR6siU' ܹpG=zsXZGY5"5ٹ̞r)Xn=Χ=YIL0V6XxaWG:Z\Y*vsh,p- rJgx~k/n'dx.{Y( l]̻I<5@ʊ( ǹ&(QE!Q@Q@Q@ X dSq 7dTr[Cy\\bch`wFZK[:+.BI84JQ8^hx% ǒ#ru4W /-:z4ȷyeI6v׭[Kkvn 3rdvYPH4:om.n-$ԨXddv槮 SYrqKiIecC`zHJj{iuKKp//*[΅7c4l}=kYҴ#}ql%[mT+w[ymMZ}FMp\DY2sq@6z[OC;rTEO\6Cn"e2~w0Z:̙:(bDQ~cQ8Ȭִ`j Ǡ$%j^,PdJbc+InasG.\=;(zڵ#bӽvt} %s+WLNdc)3=I m33rx'HIo^g8{Ҧەcx鸟6iXԛ~H5]<.Oȍ4dϿDoU6ʋkXyQ^]wLqG*TM*ăDo-{FnDn%V܂BzʴpOx]_L5ǒ5@mK?bܘrsG*uCJ! ilf1@ԌTuյP^sPY'w,QE *+kibGdgB#L%ƥeYdS,!ͼwR3YrGQ-iFHh w4ֿק= k t[V 2)Ɍ=yikzlun3zrke&|B>ԱX3+4A$ oך/j\F3ןlnth|ExC<u\&QePq&b3E\5JOJ#HiT)B{]m.`"r 9 <sj6糸FX)ᶞ8=i["}o"}o/2,q 9A4nrGoGK DۙF2@A֔cIp>%+ i$oH|&3[O#oZt06+d~YHDς}H$gVK172F$-R6b}?>NInD-n2TS?q~(g:ߺ>Nj @/\ߺJ(MۑZO@(QA8|t98MIE!Q@Q@Q@Q@5Z-FԦC"xU?m=EslcKg*p8SxA> Zi-&ހA*^ 1]$KQ\͑qv?h-;*ƅԅaNW8G==j!Cc,2*o[j" aT<57ByF{'qҁk-%nLBРF?#vޞj4M~ +I-d!)'𨴟GvWbLK0p~U~CfRڄٟG͜J(tkQsg;A [hk5G}[cxў[xD6"-Kl"RѸI9^ĐioSjU c .;G& KmF-:Hn# ,=J Gm"@#P|vtr:RE-n8\F 'hbI {.k%^O77J qSC,6y#YU^[Mmo*!@xA@|QKh_[ĒI W֖O詪7*YP@|gfmݏ}!?l> I7ckn?_Dķx[$G;_0{u*HVB$"qYg.\WzWj;]C.A++2'%hK.X+yt,;ų!poR#8񶟫iMq,q}&]88 jΓaG:!6 9;ϸVƓO챢4i0Km ƨ߇5|G%0 HO{| լ3hzJi^p!"-W9׊ҠQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQMfTBUFI' p$t$} )f#i$k$leYNAӨ(((]Q@Q@Q@Q@Q@S%(#2M"F;hS%(3HBI>K"AI+h,p9dZZ(((((,I,(3=>@AFA((((()h'HKYd `Un͸튫o2Au|Osmw!lXo#ONs^>1<3@h[-{gOF t)mCZ] /V*K8 ߎ֐#NⰕWP5K'΁,rAH=EZ3`Kѧҧ6W-s.BbG#kCPcgu7ٮm@aA8{zFN,tlm$ q5$ZmڳO }(Zv'Ն:6A5֫ZmΟ9a M0k u*ėGf$\I?@~'P!{/o0҆s0N}8Q41:A/pLwUܜპ}+6UtOZˬjE#!Hf?7zW\!HĊTMǸ=S{})%%{_:G%uc8 [ȱ2s'y98XK76V (Š((AxU:Gnt~Ym}~wM񍆩u)k{n&EY d}?s~5ZpR_S&~F&~FvA~ifT~> (ߨς@ <]}}}ՔXjFʦ2F\cQo? _ &@"9OWW VjJe 'dOH$XdX'R?_ ЌF>O3@??3Pf`Mstm~M)+Xo?8u6W(̌GNq ʀr0pA* 1*zC ( ( ( ( (#mitQ}Ej' 1E_Ū#n,-, kFɋbf=oZ[ߨς@ 7??«[$! *%OpF d-4we6ynLcJ`oo? OWF(=I6 <ߨς@ (}*=BLp@`@ ?Wߨς@ 7??³mw˻ 63p9)C3MA0LKQr> B?qJK:A-YMͼc=@3M{Q\Uu5U0>Ԩ4 "Ij똠 袊C1 pyQWf›z}r4H̄ NxlQ@mºvt'i0#G˟85E *P0!TNSr\G0IP{ Rm2Ơpq{A[GAK\Os!ĵx׿¯>C6yq 8$^Rw.?4,S|KZpqpZ{!J?SOQΧ6G֣$F1;HX͒’_oLI>XCOӵE25 p9bܜV+3γ4yƘxVgg?Gh(h?vNֳ@/[YQ21([ 0/KQI;jmbPcW1Yufi/QXti܏JT*AKHaEPEPEPEPEPc1!'f:5Y,A-h'#=ELʬYC) 4!;4J€*xjM"\,vix˂.LeTZ_t&+f[ॸsۀ>JIQ51qF*y+='@o?#$ly+='@yK_?j< PGUTPyu$zڛ`6 OL w@pQ5\|bOfٍ6qcG(]N}e-#tQj85xJ֗1p}dX?جoXoUo,oVYEcׁ+#bEX-Ff=)dX?ة7*r8#/P/$%l dMRKoDu?zrNI?ZD' n pS2NxR;FX?Vmֆj6%FFԂ6sҵ$BI$1Fhlt\gՊ|JŠ((((((((((((:)w6sIE.wh?%ZcFf$t#TW(HavqGΆghiRLt=U#L+G@}ιI϶]ϕG.Ohl)t? \߶+Qux_z4>u>W+PYۺ.uy-NF aNUb( z)Yw >?:Z{Сw29])t(`%73׿ZE'G.?? ; G"?0,}}WGw?ƀ'IU~Μ?E'U?#{G hd){:u'pTcq4iz˟ԏZO?ƒ=c˟U{Ah%jѠ?Kf(Ėb$ yK_2E Y\/LJIp eEvOZC-U{A!hG UD8FP?b9 Ǹq@hcq4}#*Z^8q4cq4b;L+h^e} )]~aB3K@Q@Q@Q@Q@Q@Q@NHHH`v&5ܫbW, ldSp" !=eWR) "-,4M,g.Oek)UOeyQsdT7zQ/msK D*@[mb;F"! dt&ґ _gX#/; & cӵ=;D?Q?РPD*@03׊[`'C#ihqA=(#|&fA=6;67S@Vx|Q:,WW o(F8ֻ n,bw 2yM=7 ,a5y-&'$e 8EH62gF-6 R74e*2X?:s6$F7=v[h{G} >H?vCLyKx>2FZ8'M,ymA=pNJmfuxnl26NiU%wr]'@FU>'lTU9$@YǍqOTȣa#4U?HaEPEPEPEPEPEPEGgyk|ibbF\2 Ϲ<71 mI$aבQHZ+`8#`}G!BkWkZͮꄔRI>/ַAO${!BkP,":KWmk2O-69uFH NUqFyPbHSn1QK{ `$μ$Exs'ri,W fw$1@}?! >?g2ԯŝ^V>s@lg! _@?Z JI ?@5+2/V\C#OC9M;:3@k”^Ŀ7ا6cRea`vn,w2f؄6kn ⹅g%W+}&eG2wPBJA-SqGeΥFdR"g}q>UK/Xt$Mk7O}ig q(\Cv ({RI+Q$ƫCi7,0jr p}"d@g>R?! Г>[s$ M,s369$q)F _3-}(?SsڋⵊI&Hbe䑂ROJsv >NwN^cWwrUnPO@j>3Z^`ӻo;ogɏʰ:kxtk3fn>.ԮBzrz@PF!{Z9dޥȖ,G^9c o)Kz_ƍ%Y:0FX#:TnK3c !T+FIqo-kK[06l !@<jj-*as+1ؗm ;WuE8SsHbK uf\jS'9Ϸ4^/PmR!եoݢoobiE6M]BTxXdӮ:?\H%D-H8xh-ʦtΧ='Υ='<*|;#._b:~O@o?Fb[!.KH p,r>foL5gU!N0;Q??GobAGO4e*8?J44h/Rp GO4on>O3U Qp?Gm^tH[6㟘翽mAm  ,q U_ '~19tJ|MNt٣Ydmd",qzMXBCpȃ 14zf}r"9qZ^Ү]:xglM}O5k'~? p-(GVI?Ƒ pԎ@Y_[70 E2Sx KY{}OT9I#Ee>I??Gh煡4)U Ub.YH7|#hI?ƀ2fsvf֒Aq[}6\cz ݸBWI?Ƒy. r=3@5?% Ksuo%!؟έH*qN1HvQscw6\7V3IG3ݏg#h'5ZrZM11ֲP1/O4h"UPT`T p瘧2K"ʞP)Bڪ0(6FS1OFS??ON|Tm??:nQ|T(OFS??ON|Tm??:nQ|T(OFS??ON|Tm??:nQ|T(OFS??ON|Tm??5Iq!)Vv#"_ohOFSECvʍm흝Ryဘ҉-BD 6ʍ@%>>4Ej$Y, G?J.Q|TEQ~ohݧibM$NM p pϸi*O0pP>>4Ej$Y, G?J.Q|TEQ~ohݧibGآvʍR-/Cr?ݴD.deEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPJq!/%p,Ԯ.Q4쮅32*0G޻*OB6>?:jV[#Լ]hrj-^@JnORk>&f(5DWm4ngMz]%ZRLf1Fv6qi:Rf]HRmxw&ZFmNѱlh1)\J9u{y]u$»pv:9>,}>KSϳVt*ISmnlᑶ&X= c\_:2'n;wyvL*('IBwHgkNI.4,(3\-p$f|ES[QDũUÇMٮR $`a'n8LV9+붺EBI-9/| c;$Z3{,+J t l%Czi[`y-׎h7$e-G Q=?{X3|,dhм"F1[-Ajb-B(R lza!ڦ蒿B[,N?6I 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-15.11.1/data/images/tone-mapping.jpg0000644000175000017500000002732212616075370017142 0ustar micomicoJFIFtExifMM*JR(1Zgnome-screenshot6Photoshop 3.08BIM resize sharp 4http://ns.adobe.com/xap/1.0/ 268 250 C   (1#%(:3=<9387@H\N@DWE78PmQW_bghg>Mqypdx\egcC//cB8Bcccccccccccccccccccccccccccccccccccccccccccccccccc " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?²N 1L޹T(f(^ׄҹ3bY# c>VҗȊ)'PF#,tR=) ;WEe<5৹Q2xn}BF [GR!._/'"yjU+#uEAw+W"yhȲrw_D";e<4dY9?+#uOuwyF 8^Ȳr?,真ƋFÂD%.?t5Q*D";e<4dY9?FtLbXр)>W<ڕ; Ȳr?,真Ƌ_(B/]~Ke<4dY9?+!u/Q_+W"yhȲr.W"B_!u/VE?e<4\ D"G"B__/'"yh_EA D"Z_OGE?p2_EA µ,真Ə/'e!.% ?_jdY9?_OEB/]~K!.% Ȳr?,真Ƌ{ش:nf"aڼcZW NNXkDkeUݗWcH((((((((((((((?_.UU"v"wݗWc\wFv_] (((((((((((((( Ȼ{\_WWEʼ߇Fv_]qv4Z( ( ( ( ( ( ( ( ( ( ( ( ( (3E"q^E^/o**`w~v5xDkeUh((}k!#$~bw{ @ѝAĹnkKƪ\̳H޾{'Ҁ-U?,uz\ߛda>VPsYL\\G}cݩ??y2)#GBv{PS;]]F;3~ާܹI@$nށHlc3J;ZCzJ@K@Q@Q@Q@Q@"v"]y0;ȍwʻ?"5*еSf=%9O=H$E@xU*_ A?:;hag<co=P֌_ˑ-TGf Bjx q`c⤢ ( ( ( ( ( k*G(#Nm폤g?ћzw+ߑb@F/`3ViRTWS0ȪdxyGHOø=P^4G}N~"+PAGzZ( (3E"q^E^/o**`w~v5xDkeUh((((((((((B20jX{f6IOUZ*}X8 =c/z*NiZK(yߏt?Yy{ ,Ոd4K_.UU"v"B]RKrDԑ5W5O/tjmF8["@Ѫ .j@_?ڣj^b24UQ ^j1\>wr29?'TPQIE/'TPQIE/'UVg} $5^]Z<ו R\PpK eF{P>O-C˫8ʆ# n2Aj+mjh-㸎KvGy 8/?Q@'T|U(yKJ0)F.m\?w&@T:ր4>O?ڤjJ(~O?ڤjJ(~O?ڤX~qk6QbB;$d@_'UVO COHc~5{Xy9ti  v(3|$'i@퍣I@Sq#@_MΌ$iH%[ϸhEcCxX{Y6>i,s~_֧'{yw;[qUFRIF}GQKh9O/tjԹң[$h^R$r2`9x}ƫekq$LQ󱶮zesHuk1y>i7o)OJ<}?G*O|>7o)Oto*O|>7o)Oto*O|>73?Z]&b?1cV'OsۧU#Ir0~ʣ?/ƼI'5t@ ]J@QEQE F0OԔP"c 1L  AcATP"c 1Ǡ(1=V H.w?@--1 cz ҁil1cATGh 1Ǡ(88,s,Ą c2Fxhil1cAP--1qq0?N*YFF{m_ր 88\P--1qqO}Z6@}c Ƿ?}ˊXY - Pd!%Oˊ>m<⦢!%Oˊ>m<⦢!%Oˊ>m<⦢!%Oˊ>m<⦢2أ@<7Bu~s?='_խ=_K8cwHĒkl%d)#i\Փ<8+?M???FÛO'*A\;X̆8mm IĜb+Ab~W iϧzm I#O-r\4䵰=rO\zPڴ-.\*f9\p R>Aՠ"cg+){sQ ";lXVmtR1SP7 d^]c+ ƀBt@FBt@QEQEQEQE_Uu!p^GM>F0_$nIχz)%OQ=S χz(} χz(} χz(} tWQ9?4_n}EןOQ=S憎ٯAMGկ>Уχz(+;5iV 1RxtVj?>Уχz(O4/bP֙憎Z} <B_5)Q=SyTEgfiM3*O*G=SyTIEG=SyT$} <B$} <B$} <B)x@yz޾?|AK>ЏI'7k'5t_1ѣi(}<F_1ѣi(}<F_1ѣi(}<F_1ѣi(}<F_1ѧf_*w( SJrH:hdFdYT#2^֒B\'3xZSYLTFʼn_6G(G2OרLLʆ85QK$1:1C )I}MvК4CHǫ=?'^?՟_< HCL '$@9zOzOPԳTBr2FG#RRDH pQzlhC2Oר.>4#Aw>g@.rqOR7:23E;\ ¿ I""^7uא| ]%s~FQ@Q@Q@Q@Q@Q@Q@Q@UddYFH_U,̚m `v9 iW=@.Ccm$r{㊒N;弚X̬<3YU@^I;ڟ #(6AH?ӆA+H qW{G5'ٵ? AGSkEA, ?9@G4V[ipqpr:7)s"r3 ~_ڟ #(Ю?ͩR?r J[Z" U#?wx`8VBB v V~ͩR?rӯ0ϩDbln g=|#^C^3C?"tOֺMo֗rGŒS%?_(v?cq*oIu ?%?_)XYoʍɿ%?_(XOz~Tl7^M .?G$v?cq*oIu ?%?_(ߕWKQ .?Eoʼ]c?Iu ,7F~U/KQ`=gcq*6?&XO]c?‹;Qߕy7$/XYoʍɿ%?_(XOz~Tl7^M .?G$v?cq*oIu ?%?_(ߕWKQ .?Eoʼ]c?Iu ,xXh~ڼYxepUIĎY Lfotoxx-15.11.1/data/images/fotoxx-views.jpg0000664000175000017500000032400312616075370017224 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-15.11.1/data/images/file-save2.jpg0000664000175000017500000014005112616075370016476 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:tonemap|resize| 5http://ns.adobe.com/xap/1.0/ 528 515 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?^#%UU9$u.5K8cVtEx=B;Jr'`Ga\xOKVz<vH.a|E}:S^CX̌:Pq^uVHnӵv^ GEEiF$cü=GMIpJmnYphyH] Mf4 j+uRGsU5[ǂ6ڼKܪ#޵cp\.@xdf=ieB/(8(ZqLZ4cԀM E.运=um;(᳕I949>|ECLdt>9a`˹Ktu\,zǢBW/푪 `[`cvzwV~"a=2)4.qIF#ycַE&q~?抖QiRV)R >!oR+ 9([n0 z/(=xWZ'Ƨ{hKc;:?tčYɩ26tEǩ_Qz/+ɡ{taqr>*G,D sҚoouӡ-5CfYs8 E\.>,DhR!O,-%Ueٕm' |Sm&|M"Wռ$)5sxEЅ9ǢBž=m ťrH)$ir3vpS]OEs\-a Kc3E+n运x_Wi4ՠMr]̆+d -{V\5.-lM큶1TGY[Qp;ǢB!^=/Oa=j63]kv!o0S' xL"b<I3^\,z运x_W' .`In8#II ; o|yi^ u}2[uHG֋V=o ˝bi,*Jk >"<꺋6, 3]``f%{z运x_W~(d"6^XC;d#z!G < ,ڭnbJ"0e ̢b <_U/22b/(!T<..运_ER((X/B1}Ḳ̢b <_U/22b/(!T<..运_ER((X/B1}Ḳ̢b <_U/22b/(!T<..运_ER((X/B运^ee A0U(Z(X`uOqv}.@W K|Z>oHvYUs?A7oj呙#6o[%i~mA#ӥz'-}y/G/@:׃c3Z =2PҒ봂TrN{Vx[B(Ҿ%36g9Z>mm<;ޏ;޴m<Z9X\OZ>m<;ޏ;޴m<Z9X\OZ>m<;ޏ;޴m<Z9X\OZ>m<;ޏ;޴m<Z9X\OZ>m<;ޏ;޴m<Z9X\OZ>mjw$DóyeO)_ȿg4m;_SGxjH|G;I1BS{5|Ֆ6J ITE7n璹Q}=ii>Ğ˄'4(?jg%>?>KeDhyxỐAA?NkS,^T|AUL$SW 8I;|qIσC.Q c~zTz0m[ݎ_sF*#z7_oG4o{ݿ=\h (sFmG%Q@oG4o{ݿ=\h (sFmG%Q@oG4o{ݿ=\h (sFmG%Q@oG4o{ݿ=\h (sFmG%Q@oG4o{ݿ=\h (sFmG%Q@oG4o{ݿ=\h (sFmG%Q@oG4o{ݿ=\h (sFmG%Q@oG4o{ݿ=\h (sFmG%Q@oG4o{ݿ=\ +#SteP:HlB$zֹ4&bI]h/ ڪ}A"-'ci%Ӎ/F$g $Lc0Aw4iz{x1 v`O6 9',Bz~h Aȧq[M?JRS}yp[R)+LjnGY/f-&%Czji%DF0#׭zq2"ʹpTUH%\[h&Yv`Ɣ*3 mmBv~N-[;{1DA|zC˩AJHdk+ -Sכ^QZ޿g *J? B6Qy`Ԃ㚧"m"[>} 0QH' -S?疩~k/{::ۛ9m*H%Z;NrDeڭʰ%oX@/[cyj_&C1>v8-b6w1U\D#jx/%9'X$]"Z -S}Fo}iѼ^+m@_곥i`b8-Ͻiu䐀b"txgyj_&C1Iv.mmS׵k˿w$6Ѷkrdh׵̻$6Ѷkrdh׵̻$6Ѷkrdh׵̻$6Ѷkrdh׵̻$6ъk˿^ 'G2CAiO T֪j7_-K< Z'&wmբIEQLʋj$*d9/ 6C̓? U }:<~t&&~ty}:ɣ&~tyC&? hɬ1ߝc}:j!?}:mLM+#j9TYܵ94SXIOLQFjxGm_ؠ ~o=jb&U}{}V/Q(j*xGڭ_ؠ ~o=jb&U}{}V/Q(j*[xGڭ_ؠ ͹b@pn o?hz*ݿGl >g?vm?ƀ'^d]ѺȠC@ E%RRE5eTzG(j*!qM?%/ֱA#AYw XZLz8yYZucq6P~!uki+$AHo-3qPTuyΞ#tYq 3ުi~/lDMYa\7q\,at.qZѯl5=i:,\J t`C,|{5yG??1Wl85ZLI̗r]%{`@c4ַDxbdBm+rG 23V/5h.|.7-Wf9ިA!0)])b7BI |܁֬jwt]o ,UPs@ _زZK QLbW8r$U+V5k9,%Ii>b#ylLw?u f^7P~M6w,c*BP}AEP-K撔P0(T|+W^^KŸ?rm_/ٝA+ ?OxsEӥKҼ;Xaܪd\VjS_Igd%Mbѝq꣚t=F_Zŭp""dc8&kw[xʫ ^M/vǧ[b%sj_Z`m?pW;-𭵊(jZ5{/IdMԘ/ RHzcnyW%~/?+4FPmmӋywQ-:"S8,yhֵ[JD6SwCI-]onQE,vnܩk,] =KNXo&[pT(91]Wmsk2JVA$P9Ԏ6-^6{xKc֬}}`bE)MNcb5ŭ\q5tQJjf@ = IKO*mC&-CчKdX?أ얟Uέuiuך[HFD"|Wt,$6:F< 1}}}`bih:~y׮bVoeվ&6Am;qkjwX rqr;G+$Kgh+Uۥ͗bPw&5 ;gᡗaγgu3Qwd&8#4=jS2Yg<`*q?q[ b}V& h̹ܝ>laF5$ezoSό't(\d`J}EsŰ8?G-| o5B+KK5 R୧O>=s=x ^dO&&ŎAOD(Ű8?Y^%$;yr0AI+/]6mK0nI͸PH&Hx(4;uӼIVH.#pˑq\Lw%amy My <#r8m^qBoezG9@_S=iL0(Si€!`vtYd@Kx]F׋g"Ć@ۀ=It':o&d4m&|j7q~X \*c`b[41+}jPvi{PhRPIJi((j֨W~__MK7(d1ՐEsz&R{Y-IQɖ)'U!I=s-ՏH hbpa>4ٮN]Hiu F O~5(?Q(?(,@ƔG__b}cbE>߱@WUS_m^Ɇ>iV~G~b}#S԰OJ5K/5oQϤQ(?(:R[dS}iMVd_?b}#b}#b*kX_:Le(?Q(?(>Sh C^JuK_b}#b}#b+b_V__b}#b}#b0eM:}R۝gIO$֙, ?[_(_U5[iO9Ƴ-ڠǥn?v(m?OI#b A/PxZPb9WE/h 9|S~i?PbG| `?Oi YdU;V`Z>*_-!J(ϸZ ر7fyĄ8yUxEv3}OW֮f8v8\($m@&ڶ湦}n4; |-28pOZoZ,v$ic t@Һ z\n$d{L͌u?J^3]ʤ1Qʾya"< tn*?"x`6s=sh^ROlQT6?{?(cmEmQ7ɥC"8a#~i-Xmi|G8*U'>AwKu8WKvp$tǧUK:g|*#­vS(EPJ))E9z 3WQ_5(C: 3Jc71pɬiWtwbXe`7Rr8S*}R'D,=IRpv>մKA[{I d*Fz1egڀ3Lϵ3F}4Qj~hgڔ\ҖĞ ~5ڭ6^Vp]i0Brd|eAIU2m/zfGkF7 /uƓgY=ԏtHnI%d$H:s8ϗ)jq"3WRè^cgND?#EiQ3<,[!W\տh7Zװ\G<-7:Ig6@#Sh4LP(O梷?$ v~EYXa|6'ێxQgyl е ͎t cZWsƼh#"Ќh'iy0YYIGH1—- d*Ow^!:C4n -J:h7SOZPh4h'Hdwq~Z}'O]R|DdmݎiRכw❭]֚ifs1?:4 r^m9d7Fs`(R6DvH۴n! 8+mSLW7cRTk(ռup-VnyS@*V#QK/M3RY%>>_IF)"9TR$4(RRr>_f/Zo|k9|Q6/ts c_C|fM2gBC,,AF$_ꤚ9+=8U%yN;EڡOQZ4w2Bg#VNjw B1h1F = XTW MH}gC[ m%E/nʲB-W^\vF |ܠe@8zfzM|p|'+^C!k\sq9f04Dum 8Nxvop|'(\?? 'm<^F'вGk#Hx2qP^xE9y^']p@fA } lk?Y>*ҭV%,mas֬iZ垫4ًc@pv<kp|'(\?? EU\?? >­Q@~|TP_|p|'*Wp|'(\?? EUT?? ?? EP%2˒>Sڥ\?? EU\?? >­Q@~|TP_|bU}F]OL&4]-&yZD(m#U2G4.ORbX뚝CYe0xeaeq'uQPi+Ö:z$׷Bc#xbNCl?XWn.{=-6اLX֪o Esg ެ\!$mv9Jn?k?\+ಱfѡ]6M31©Tك֥l꒢o[;1gp<.t_k?J.? ɰ]wc }D-6fCrxh->wI0cC>pz{SRV'ֹ/!}}SZ^D%J:=(j!H0`tw>3;nv!a Rw?}ֆ=D7ZVB՘a@ps\Hd5mt^_0hkɸ/v]E]. ku )f"A1z֮ !9Ȣ'R[bdwrMz-rzdj7O!mZռĀz-K-ZY2P,pI ,i,f9QdCXh -OQki8!%6w(럗ݥ\QJQF(/Zo|j>]fh|~gIsKc.QH&9>âp͌IX^<$aP+]v+3Lキ TՊUJrߙ;hgY!$L2xji.ag$,hMG\Ӿ \L;x諴`r1cY7MOK)zt]ʐ;1m[NP? s+:T|A/E{SPXo)A`ĀIst"/aߩv܇d"q/Cut 6[DѫVkKs*jv-.G?/U"^.i%l 0y>Ÿ]ܢ(QEQEQEQEQEQEQEQEVWt(,D$sE&]2;ժZƥoi^]򐁄\1 $@iѣ6z1BǕ6=ՆY5=+x423Tcf|]`,"?h(ǝ;qt8YĖQߏ4FӋcO,#_֞T.n@(\sN-oh'7ySls3!Aխ]TI%u]4Iys2 Z"Qlp S.'<ڔotX<ȸ6p*x-{MAldQvaRmH*V-n?tBOЄC'%ϖztizipla~hCԴQM>5Pm1ZQ7*|WhJT m5>P[~f[,Q<} %68? ?֊Yֿ֊C*}V3߁ȓA5yU|y;^p'8euVFR8} {;7N;K/#OB6@"WY9m'vV6ciolr? Ht=J;H5dm$,v j^wPhۏLT| cy- E[ť;>P@W_(kR5r%bݾU@ǝ$vi0mHXM;gja[UZ %Q@(T|-W^^G?rm_/诿q\A5Lkt'!?65<1dr1rxr^,7j?eúQNƘPeVKT.nYOOjHk;9m N}LC*eq*y\̓zE7bߵw=(3C>%aiEǒbi4Fdeڧtooʷq~\sV/>}k+X%t{B?@ھCj6`3XVk[fU6?*Y ѬHm]&V9{1v IЎ39=3Ğ! V.i+E M6H~tbɮI;01j[!ؼF'1X^"< RGWVHds+~R3ҡiE$FKf>#:tTudk";6C"\q͵tb~z^o1(60I>掶_ k=/LJWQJ+ #'*..$KtžQx$r";*+v4UH@-jZ:\<j֨[~__MK.?&V׬,.9,X:^Y͏(Cs^}uOj׍,3^88Ph(ٛ`R6בzPw%%Sƕ[˻+Ά0%h.YMV=CK=@/t8].Cepα#P{0tgFww6*&-ֹ n,5n#K.&agLqzdnϭ1V䳗Qd:"޹;k$pƨgUu}0j$%Ù!HؓXʺ3'ޣsxp\^]O>XUrt*E".ѦIg rIZЦ(@QEQEQEQEQEQEQEQEQEQE|G,<2,-*HɕDm϶kWFA)xzUGKGc<5'/DӴ֏ˆSh#V[hr?݀;c"0 76kDh1#SG}y>Ʒz k;~f88iZ׈$5eOUp CtQ[xդ(^m.ks!.QT(%NFq(/+IA~=V BrKG'4  Kc |.Bٷ~{f6[;Ye% ' (̠`9Q~4-t!X4?Ii ʫ 9qڳl,΍K;pņ'p98ҽ Y 19 ӡff~ianUshoןq> \PGD3$Z7q~Zd_-7 F{ Ԣ)i:MO[%\ɰc'cO@ jk).8\r3ZPpд"#.VUt@{h(P(֨[~_jD__&7^ukØo.ؠ'p$? "4DIBGlWk? #,I@8'ޱB-ͰRn]vdfe#>yvhH2 85x?oɫt` 3Prrs]co=BX38U-[{*2 y֑x.nm 6ʦXE30%vrOCYw,"LtY%* *}xLt˽F+U \H2ztk=݄RJdR#;7κS|Z垗{xKJ/# cסU ="QH Fs&с@(aEPEPEPEPEPEPEPEPEPEPY^&7:Dn%iɱ x'ҵiFD2 WCGGT 5 .m&S}+HC={N}ݬShSv-rp$F*Tk,-۪} v'j@}S4[IdGP՞[AIRN4ȵ.kHOV9=빳W[XV 76'h?Av3ws\ZCKyN̤Jqu (Š(((((((((((|[Mu2^fvqg?mU sKXӞEfWWᑔVh<_isڢߥr lm7 9#kJ\IXfPZ6`hrBĂXBխM}D`mیdcn0it -R\e/,`8 (^A[+iޫ׊ 2i@-d2NI T<7)/-n@ӆJl4m9##;[od C^wK!FsT'Z}G},חI<ꀬ!ʡG<󌓌(*k^ oM9,("dvwTzu牵8[_mB==(|- c[ uaկHI $-(r3 Ϫ]]^uU xp8BH?RԿ5d{Aj.`* UP *ApxI ʹruTY5kWCٻ|/u+Jxn6>7`* [\Z(sh2_S3E(#r/֫Pi+hSAmJSkW^]]BQk3s ('M9z2Fy_f/Zo|k9|Q6/tN:2C4ꦘH8M2dAhY@%küIkd:=3`?1>qu*Stٶ :jIyk2j9ڻ_RWI[֦O.ʥO,BW|:H[ŽIq`;׮xLm_F990 g"J!zQF6V-rW[H݈iPp9'ڢz$H+e9tF0]`u'VrtBTXV ) L@U;tP) (((((((((*q=4t\FMX8u[RK,fK/' /9O<9YX`p7@qۥ:+.~VM8lr1g+>>ҭ{!}\ 7 lr8լdY8S3T(뀵]/"y4=rQ,tmqp`u,8K~MNSYIapH_0|9jj~" ho|ܥAkĨ, eg=i= r;=Bmp'1©榹oIfK)n c W$ JA$׬"{ 4%Qmm6/=)~RR罎T*|돔%TзikGa0?>_O)T=WYQGj9uf{<f7n4Mk/`)b-#*_y|Lttcfo?֊C+^}umO78LMh}V h Mx׌+;M:]GPyn&(t]c FO^2>|?5+VPԎ u nʐq H״QXpM G= uxfSK O 6@ @) n5\7mfWYϿɑʐzGZ:U!S]Œ#rbwV掁JQIJ(֨[~_jD_q+(xJTB0p~j7qx3.q43tӊkc쵍>ȴYvڧX[KY'tEA!f?@:ו|2Ѽeeq+C7bo 5Z ^>y XԊc/&hdVt VF \f4W6LO6pp{}OqigIo p6UKn #W>+/̈́mi6*m I_wu !;ng]RFx_VhHID[HRs>:qV6)viy~_,?ˊxoZKY[ω[ =IoKv T3c5W)纮!l(ԗܥ0ÌL3}Z_ ]n%IfĪ[$ r}QoaKKyB e ʱAw8#t;;˙omvhddSWӚ($֡fWTosy߆5E7gwc۳##5iZX]iz-ͭDZ>b$) A_ޭ˖ 99?ʟ #Cs) !~AyYD @B?<4K=6%V܆6lI€e~nǤZ^60@p?f?"#WGyBWkbAERQEQEQEZKq"HXԳk*/ix~YS7';c_(ۊ9x-($uFT3 ̊MXZirW0[\$D29$PQ vlcD}ɴR2TQҶVk5)$HWqYWk(&zPW$ٮc3Ƭ8gr3gw98[j.ٝ%#L[J+gGoEF]{C:љb(%Lrfnuk ?tֱ#?J OM-滳H4)*B:OwSk0.8WnR eO\3]͸|[~mBݯKYAP:֮iV^^Hc8$I W]ubcӯM-59Ó{g=:RBXq7)mO.E}< 迮/çKx2 DDa:գڦqrH*KẀ6\^GSYu=6T(Tm9q՝LK.X-RkI 8FrI$ to,/Τ >"A3`QxVXB}c-sx<.i{?uM/7>f6fsvvLҭo,aw|㍮P&i!{kbS6;bxĵHʺ[Xu %Юu6DN/ Bk:E)1g’=qGK[[4z}Ԑ,+>"k$70g=;Y^2~llypېdj}nJ/\Z+qpA|`Jy}:6|ZnP 9ms,p,HUG'y, yͳ_m d?0s]yn<:Q<ռċi*;'tVx}6]9 7^M̦!)|+{ lB2'x3l/+45m*86H- uN&'=6&[iA8Ƿ~Ď溏: [Cn 10yj,qOhb[K;!E!L9#v㴜1v5̓Wc~ 18P]N*`h֨[~_jD_գY $@kC1Ul @Oubng|&<-gu72NNEc}kiws67ݎ9՘We"Bc_$%hW~eM Ƀ?{z"U;d/W=)|(W]k^XǪ$ѭŬcQh1 A)#?g5GO5iqL[\F a Tq1ZԝNy2GU.v㌟AOy~wwgů^I5I#&<$ $W^ԭjbxc[R`p~\G&cx K"&sqֈR);Xdd~亽t#]I8K3@de6ϐ+t85x:L$fT€pmnO~Ozin+֞ik6;-,P>3vXj? XXCsQ-l,Z) X|1dۉчS">k6!K+m ѾIrG=)j/,v?ڋ?rZiyqbfw_xUm᭣e;ǩNՉSռ5\e<@qWIFN{#JI)9*؅1ZPEPEPEPEb>PB:l_pG^GZ-KhJ326$،vTWZpev6,h0SEFC8[~p{n俼7{ZLs4HF 6р3 LF @,?#%uyGcEC^~-K~hmq;IoxB [lÕV> H-tydkGX1xb7qbGS%9T,y ,_,`dkwhݜwY:Y^AgIsv=6nt}2B!O"{`͞y-ʅ3Q'wM3f=f-9ϐB@QBnz)kc{$2y8&籺D++nGN;PEIKT$6Z%LK2uhz)rqޠ&A"+? hQU,kH~+*_iז23IċBJ4 J(*+sr s2Z}Ұֆ*XR 1Yo -iz?sa%A-|o. W(/e`UM]b>)[=. V8 41OxHUu u)s\MjIq<-Ȅ$+>"\i;uxrbgzUBjj%fm&dlvl䖉bPO)L01BwDRqW3qI'ٵ5!K21Tm|G_\7Qbq }NzUv%vQJGgn3%hNbC7ұ(?Ƹm+\Ԏ9ڋƍ j~1ZOB]LQ;3P hG[Iv*HJ B[Ɋ8c;T `gW&嫉mdd"Xy ?cWm+1,mL6+n m3Q6텬O&lōY.Jڝ4=& b@͌'$kFi[Z]]]>w˧jb(>Q)!xx?:ŭ('фLY[f>`OhZWݢ(((+!9n$P~qipۭ6cAeN͔+ `s]8Ix1s݋Kqvz"]3S4Q+FTW esyx[mZ=;̚ѭ|,#P;UkVkȶ%6nf҅Wa՝,$[q3Iikogr$ &>%֮^{qnb.vϭw.mueI-% 2Px$-/z^tͼ7P471G4/$[ KX7լg a.Ē*@(:Y-bKi9xVU vqAq Fb ¢ '{}zX-*mlMC1G4\x_@KٛR.U$pLRh@:ZZCp̱ª=y4]vwVdĆ4A!v_X6&ysB9u2F2_ F]7B Qx}ƑO qӒ3S5][@naf1z\5Ԛhs&vб+?)}k+Y5{ռsD$RcJ0b>m>_Yxvk;:dAZ0Xdq/aקzK/c d1$I?Ҵ֐$~&c'P;o f__]W@͵s7׭pPCxHW>a/2r1yFPMs-KܧF<8R8zQe4gԡ[y#i#p2ᱍz6Ns] Ev&OGnpcyW| sp f!HK8 wnGJwߊ7.+|ei[GNU&*A9=Ҥnlu‰ Xʆe bIM'QRPEPVm>jY CMhbf7M30k? ;2ugoW?\I]}ZF(E!s_=se,.<%[{q1dL$' ujma-\or=@7_^7ӴM[K.YԬlÆojkSHӥcL f$w$)ۗKwl:$%ď)e,\cSHm|'IUXsռIΊSJ 77!i$29P3pVikc4+j ~QPp v']Ε p$dI0R3p9MD6wo)vq+ƣ9 ?.1YrxO9&PʚXf(n@ g8)_6~"M1tq?IJ_ "0Es8]3M+ T+CM֘C噶B/ST³5a%dqw峑1wJ=+JtzN{iiJ;Tg>XTmtaFr8j ""gy#eنGxQhzj{iPIp T +?@k!q[$kk-B2\j~xwy.q!' `t7o{y㐼7D`F=1V}"GX"t#+61g%Mbk%bYN$RZ%Ie.Fo{m^ڬb{mH IqZWS쥴Ko )${yUұWˣ}i%̳Eo N8@N9  )t{/wY#V݀ByjjW mRfl ᳀e j#m|70XNSR4@[3]m(Os,؅ꚑ{3j 0p H'P/5MEIᜫZ}h wbc;кXo\Z(sh2_V7q>L}|q;}@&,^yM6]G\^[-Ɏ(FH猟}:-)&cuʐ !#DԾ!Ho̮FIY<3mQT|A&M}܈EpP2NMQkI幍Ί'y zf2x(dg`FF2^ESR ^;G⁝sREx\j\7Pͥ5>77C _Y[OᥳgdA.NN3޺wKX%}e!Ё^%o0ⶁ^E[8H#.skon౴Tv8Q&ۼ2ynG1nm뚋K𽾟ug:O,nUC:gP_ljZNлN!nOnAKo+}Y£1.7`23Hf$&P{DxڸX@ϭh>MI^UuZ>U{~joJO2?#7BU x6;8/t8g*cq ?<{ЀϽEֳNIt Uhr7M[KKIfVxPTa>էq2Q %n1 p'|]iYiVCĦ6[d# 0O,fEӞuEDzgq\ŷy@ȳX+^fEdE\A]jAf-岃uY|^a$Nޥc/\r(.G3scFkw_yrv^};B on1P+ |okLFIyy-.A6*K{)$'YќJK  _QQYSj6ڴ%R#o穛*~lsLĚTkiNPHFv33G[EcGmMMtQC8ꡱG~kf ( ( (24Ȉ6e 8*qֱ? AXr>fIԿX-%FV%`ϦMQƮk,-7*C##Q`%M5oѵ m>՗aXmCq8#ٻ%FX㌞٨z>a[=-U8VAXtM_G $b,m,}{ <+iꋨ,EchlB*Z{7V5<+ ́G *r_YO%vJ|U VqrZ涝単6x#0E j(tV~Ei3ye$OX}Y=ܱrFWv`H sٌӶn^m]E/Lc#wUk2 vFȼH#;*}/>*b0dpz0N}^_?~GuAYf7 #0"HRƳ$@P0ܣ^ۼJD-`<ߊұĺӯt.$Ċ>uh>+S-%g9d0JoSKH5IbE"^r8'3ro'[Y.RK@וG {Uy<_4V N'<NޤdΧlPo9;0ڒz Bd.Sk?ڒ?gSh9=zҦjSn:*,a98sRXk\xGKv/9%gJȪ@ 6G{"$pCc h],7jMQ7HekϸZm"@$$)KϸZ8m \tY56Q<(ܬ=7u @iS]Mtψ  `W~t"I6vĞ% !RCJzPA\0i5\wyیjY Z7[Kl耜e9 ~(X$9d~os_[O]6[sg&ڤ!²a1 `8}{_ |qQ5wMODEtpкq'+OC"66q܌Ww#FJ X9| 0%?JۯjwVIa-ŒZw|2`BsjY#~F\x]g!,%r >QH.1\{YzF_\_[uX]p{Lk cTh$P&x 5_Ul:]r)wfnOqZ['t22 zaI)4oUeVdYdp"e|Z6攚H??Z6VEWXbDo v{Zyḏ?Xg h([i DW* sZfxv >UӼe$2J囪mzhLP)mR@A=Z' iIg[iR+y$a# T5jb^6Ue`<[HX*Ҥ4"D9pv8[WS-nA76 pIu[亞љF[nHsSO_X]iӤ"Y6@O7 XzVI:dY^YydnLfn:;hED:>v}Zڲi_\'(PwrNk>A֤}Rⱆ,V+b/ق8k|G?glÔsyh#uw,H%+,/(sܧc&&bŧϣX}Y2<b^j; ˨sZ*=BBwy`׊F S<&eDJ"(/hdՉKë 6K%Dr26#9 1imUbw2m 6ݘ϶Noϊ-KtHb%[-ŧG/#$ 鶒2X&=2)YrhZO[+\Hcʒˏ]y-&VC1#ҥ6i湱bM1IXw9uW,(m+jQ,0p2:./mXu+k[9 J*@9h/:ڵ7S޴Wb/Q^c*'9ٓTՄŠ3Fi (4f i_UVlhήX՝ͫU$8w5-4Z7K'θgU|rM}q1e({f%a_ 6Sq\gSE׭ZGe ukcur|K#mT8,k˭{vzdl0u0cڽ>X֏4A]=J} [9۫D|RXWvOݷ@~a޹k-]ZԬho[U IG[(p<g4Z]u;fB3`Ӹ_}M[ TY38 9'h.xG*V&D(rrw uAOX4DR9:ǂvu1A;C qyE}p8髨AtA~F ȅ|?j)]?ȔhC ((kWvzMm_^"YW{}IaxUnt٧LmJd3e P9][r@$TtdKhyVe(S r+/ Ҵ[{q%˜gB@v~5ngKkU q1 huv]@з+$lOЎ*]}RG:3׀sAOUGK{n$$1s rW]++}OO OxB.]^fR˕$g ~o^_ۜgVRoYqIskBzk:e<0\W+ wdO,$q;Cb Nd408䜜vwh8ʐ6pxݽǖv }G}jNHAX\Ȅ x䊂EPU^]Fwnczf5}4n5 3q'܋]ɩZoͮV G\掷 C{ &䘶[8߻T9]Zx42K)E\+HQC60wץvPZOs-PIqȒ@Y>r*;MWNx,.'@KG콹P 2oֿ֊CA1EGsqF[8Vc@3rFWN^iV5|ؚ0Bg~`*9ތdQ@UbteyʩP O z֨jV򘤑Em!_qV-n`̶dL#>( tw(Z(n̹cLgMIT[;Q#}Ֆ6pZAcYG|uЈ+;mű'ڝ Hė2jN{@;UKmRQR2z$e]lO3~sHcC&\9KF((6E~X9©3}dxA$(ϔѾ=vԵNnXdHz ip<6Q;;$V=i HO8 bܞKP mBa(}~4uʵ&}Քz;R y/Q],!yQ,>|dp@i#䘤IZ2FT *eG"b 9>ZX:ck+'ɐmT <]Y ֠.mB{BʾX(FSnsAVMJEb}Rhm4G鉩A-@.d tj$Գn!Krx*Ѩ$qo'8I#!Џ@Xnl Y_00>Օw-L`(7ݏέ\%yWrD ?y"ho{4`q )h{h }Nj=յ&/m2VÑSx^MojKke Ϟp g9[!!Ujp0*mcH`{[g 9di+)h h$8k8%7HPJX:66yq<ڨ>aIt[CY Sc31Զ{ E̎QY*C~VIɦPn8sW%&9{s2ηk֛8G9 ej:^Լi7`piؓڡ,R/Q1 )?@牵+ 9;Ȅl~fU*T"7jڄSC]3 5c*mo#/!W'\R:=2>*r~BOJ~;e6Y`{i-sui..-9I1,[!x!9 3ﱙ'o: U}J5ckYXzm- < ˧jjW yUh9#`\x:{V}ٹC rB)8PO@!YCQYmñQ귎[/ _lnh?tq޽'ž[7֓-ۀި{v7\S*۸8 Mt'=pyySwSҍ5hmt0:ndivB>U' e:IGv!c >$#'oH/-̊)a[8uk4-![<+U1Ko& UwbO#S;yf':9jziq%2J8l$1[~kw‹oo3s "ŢXZIV)3ےB'z>цgp"cc=4|<'>m"լ-Ixt.cG: < #|>[_29d ea6Yܴ'4)+ߜ/ y ^EsiH%pz{WZER4-;Hi(B#9 3Œimd,Tv_&ЍY*+e #Hg!/[\Cmۻ#'[TH5hwWwlw&ryB8kShh ~#jަ^Ds.Hpy8ǥu7KXo-'MLIDZ7ᖭ[B$Iؿ6##k5 E=haeoih;kx(vϰQTӣFVxIa^9Hb[K2q y"i0$,4q#$/-kME{cօS*!u_jm94(I>EXWkV~=w2;O_ҭP׿-.,,T'7WCE4cּK~9,/+>3 @(Fl|0ϡT[sy!DT5Be=*3I|\#mzdd+ ] O Zwh̗r[b)ݓG#֡~Q}S֞-S_Ai% ,A3ڭZͯK61N=е;1^e|COtok@vr6r)<3sSЅݖ8 e#ǜ8(ZǧQ_뺦w>wms\_moxG-Z)RqgPƋmQOufi#jim a݀9#U[\tvXU8ͤipLMw4bm?:v@`5~s"<  ㈮kao.h2*v<{ $kbפ"}.HvEw`8c$kQ|^ڒHU#9 '#SC͜Q^}~Nƌoqo=F#-橍K|ē z Zź@f%kY7w +}.w4Ž军qivaSo7g'p.1ްZ}A G&]AQj>'4佶Kn $1* @K0zxQ?xmmY$ i ~c }RM&;s!Ieƃc9l pG'Vl..EYUIƓvZs41ؤ6\WћhyI.5beEpBI9op1ڛV_Մu$xU/ ko*?`Rèۍdµ|/.>mxA^9F? ,: P y^'5 $ԭBrU`MKo 67-䷖Iʄ #G+ѯgŘ) TwRTz` 3IE-4RQ@ KM# 5m[ߥ Fhe[ߥ ?,?/Uњa>Vi\m[ ٖm~(̰+oVFhes_Ka>V­fQrK

    bO 7<@^,"I$nfӡqy*>sFէO 7<@m Ap.zl';"S0PH^xϵly?xP6:ukmY௕sֲ`oJ[c͏s[|o\C.̃8v_O=Ei:V=K0$FNJ*m?xQO ɏº$zcӡKO0d|ǩq|T~m>(}9Z;|CIд"M[b93XC[S$I+?QSoy?xP-ؐٶCqtzAC~[v,]zO*?<('DžghU=Q5qnW?r=7B4*z`O 7<@DͿl1y"i@I#Ҋ\њJ(sFi(4f\њJ(sFi(4f\њJ(sFi(4f\њJ(sFi(4f\њJ(sFi(4f\њJ(sFi(-6@I9yQE$qT=Ry GTcW:ognqM$%BA&-G6qځ#/74҉&a-rFzeG5Ù!t8]ػʪ8@De q*dHU$5^PԼ3{wg'\eJ@<yѪ6.Q t0KEicaUK^6oKjˣiJ!yaBwn^YA$2cgߐOE@s=?{@ٿUefUY_"<yv># p9*!㾎e[Up K(>v ^6oKȉqo$ H:tV+i@DRJF9MtztC9%q 6(((((((((((((((((((((() .veuk,"0J,&pAY~Uug$ Z\mo25Ot?]s^N[YZB  OC5Rޤڔ7f!e%J` )ں/CMS6Amg[]efm,IkCUүu_A5XSaooȲ H 8`gx|17<2$ɷ8udwiޥIwj˪FEc=[Ymi&6/HXݷǵtl-K0 10}S.h佼3grFsdIZ+֊CE%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@(Z( ( ( ( ( (Efotoxx-15.11.1/data/images/paint-transparency.jpg0000664000175000017500000003372412616075370020373 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:tonemap|resize| 4http://ns.adobe.com/xap/1.0/ 267 262 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Zc%UU#UXMO֫jz]ˈ1^%kͶR?zvg`U=x,F'4pX%Rz#n"0eS~ђ|Z@8%Tc8 z$/Ou70I^b~MmFω*:mičaYpE!FR8;T`~'"vUYA ۆ0O\ ;R+({;c f1is άJ㪲iǢBϧFvU$c$`9~P+U$ub运x_W5{[9dԢY!%?e0܆]%5;%.tVkqҪz6O#G˕GxExo5j GfwkN-cT|]˫K7ZIDp^6;rO;_ z运x_W/<7wrn 3o;E\~.RGԥ:-,_3\(3q=!F运'_-nǽ6NFn$X/׮u-%掠S"X4f2pwg+z/(=knCDeѭor$8Z3}^Uh-bv  g"ǫo 7ExxCJS zj0Yd ,@d}j}Ş(IT$q{qX!F运'/Ckj[p]Ko$[ jkkX!`Qp~`_QEyw<]~ ho-# LZ..运_EQ`b <运>`aڌNp;P/9E0`/#ܙRia rQq؟_VsW5Eޛiv]\/rz9rmf؟_ʭCYnNg%1i `IR[ 6'Es6۝˿D 9B;a5 \HeMQAaԾnsk؟_Q?.r>n#XB\O`/wq 3i+؟_Q?.a]7V" Q i-ATFbq!FBPah?޿Z'J[Al$ 3>lO/(؟_Q8 SZL&c[[KzeM W|?iç['P&]k؟_ʍsmnV}u6ͩi ƺ؟_Q9{3Lkk)ET: qVLO+؟_Q? ?V KRKJTFTpk|+gbq!FBPZ<[[Q?syֶ'bq!G(\šosފ(_ʊ\-E-%bL}e'sonFCm`88Bۏ(ž>:PF!᳖ƍ~d'|Rdz8nHuXf1F䟝rGBoGQ\?rMiB0l,~2u*=$`Sd{xѸUĽ8ZepiԶ cV+(5itytGw:ɍ`{iEqgykǥ.3 Pʹ<{iã$RDGDƊgwsAiג_ڛP6ӻ'T6?4S eX,r3G4Q\VOefk7IHc"66K$hɳ p?:,;JZ"krG%eWj3E%QEQEk:lƓw]1p>'/ntk2wkks":]YMGGpnD`H#$n*4l5g"Ԧ[B@S|!w}]NY/Fyukur#֢}cz}p _*Qbe`u]{WwR%חR(RPO*H\#횣YZ۫f5#n>cfP/qrGsw{4Lg_>LuV6?5R㩦^^{ﭭ5 !`YW#U ΍&դ +c@ 1pyuږj-tFBۆdL9M쮺_Vzϊt7:T^Q]̩ce4?#Y8s(!She w漧z>ZO>XP@]F3 tPZ^i^->MFN;sU%no N?H~]@n5KUr&DjlI+&nb{袆GPV?90%#Ծʠp+-VV.-SRKkMJ8#˴x(_#bG{&=؂+nnMفde +EOnl v+LogZx[tjcfo緹vUQJ[ at%7;Ё_j~6t&{eԃ43Fc ?xyh~4uKP[{;{ƴpZ|w(sl<-Iy;HIC?^}V}%,UZEW3U8SV'vxAYWI ,%S0 Ϧ3V%ִȓPCm0iѾ@kM.I$[bIJ)3ۜgV:kjZnʨ| tծn"{,c)(bcyt~χ36u$#O4b'hW$({_̴,[X4)&Rs욝C0 #z\}kizFsa NQ2díb2T.l.\9vanwCkiܛh-i.] ϷJދZhZ3uapWFpOuev&1y c)uK= ltٴ p q㧽 Jt_˾ps8 xgTۧ[9 [yQ:9҆$U@2FGU׼CZmmn^+ndPǷvZpƳ{ _/?Xmw9xRpIҸqrq۞4oe551=ol:g޶kmZũ]YOjdhXll-.5ƅug><(Xz}Cy^IgW/_ju/4;1Z&omc5X+G^w$GS*?Z)Rh"ȇ+X7w31y$xYRkgJhm#ȣU$$?h?AR*O%hF|u[ֹ^{Y iX gz!RKuVk OOt'븊B$z=JCH!\ u4'GYS=W[Ě+spb0p"9o =ɭ}Im./.O5:5iX 4v\oֺ k޷ )Lk}oq􋇅4 ^"0:v֖֭3[[C L1R}$AP28gZ?/;ƀ\KG*B&p3]SGg-%B>PT ׽Wm+?}4q3\BOya,M#ʌ@%m`F= k^&_k"rEcAIր,(4R *iJڥpin>٬hSG[M39 rx_hOm%k:{A ڻE'VԴNӨԃq@Vuι\jvqAr3 2Ӟk2Դ95[C2H,[=@PIEs^񎝫^Y$p^Ki/:ae*6yoi;C @OMZ[zV[C&33w=3PӍ>tqO:'oVuοm-Ʃew#t,(U2R Gz(P(OMrj"@ې H# -+TRieHgWlOOQk}Sr)2iݵ)AZ)ɦBN@۶?GOVɚ0H2#,)`G9 ?\;??Km}SOW?E|={ʋ;[  H,}};Pig0jեdB:ƯC4sbXVa''*nq܆"f 8~͌ZY-հpO3іdmV =AJ-ܥ֜ duT>(]h}1jZlYH,pG,w~)g˸$њAC]X}nyQq Γ#}k)"y"rIV?4?g$X]c6s!q<`⽶`?+\BJK+k= ~#zhl4;F "X #){+Xm() NoI X޸?2ʠi:1]DϷ.ݧ+Xg^zax(.6w56 n3P_o9@ <;x~bѬ!IHiCM6q=xԞe)B+,Y\XA=j\kt1 38Ҁ4qET>*b|pJ}r Egv!Eے#N<=5~'ݼJ\ZK{ ɷ:Ǣ}=]t[%*"i= 7l~lkI\+F,..oT0#$3ۭ35yuePUUA@wWyúv,&kk Lѣ" q|;)9m). glmlcI\^Q M61\].݈_8qȫ-4]ZȞtn co;"cO跶H5 R(2#t=}i. OyfѴ.,o7Wઝ;) Hrs]u\|8&o--Z 74m^zVm?E[62vcpG;kv.<-j[^Gq@{Ѹ{M@zQqhSFisg34S-۰X\@NO>$yk B{ΰM$*<+_3_:dh-E$,0y'vv)Moi%$ ?rĂF' y jo|E͓Q$RtpcUWxחd*JΨK0P}_iڅ ֘jq@3L>} uMA:Mh%Wzw>/'jn s)4dzJdxeo/dK9p;p"q [ O|]=`дm&MWTx ˧q,s=S_Hz;>y"Y$O)XqAQxj{ChZAg$y 9k~ĵ8{]92*I:KvR&_U+ sW%vt)82FF ǩOP~(uRR?fϗ;}ZV7aY^ѭ~W+i6t[ZqKN hjRjk3t1)]Q$o k1F$R2_8`/~%mۻk8OZ4a5ڊCEPEPEPEPEPEPEP.cԃY (gvկ iG}nZϸqA{7Z*[XK<!ZVOJ.b,92#(gOVl'p)h?>7G}n( tZ~RInY\((4RQ@fotoxx-15.11.1/data/images/adjust-britedist.jpg0000664000175000017500000007322412616075370020031 0ustar micomicoJFIFExifMM*V^(if%0230#Ƞ01002015:06:28 22:16:50Fotoxx:trim_rotate|resize|tonemap|NE http://ns.adobe.com/xap/1.0/ 8 400 800 2 2 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?b `3i ʹd2fJypX^kbց?1Lj&`KocxgxJ g$E ʹW TxaD#8 ae @u{/sҍ_i# MaQ4RurFݬ:.M+G@\lĐ[iE}AYZiΒ<"pqu&fRY68kz/\ޏ~.- j $ )<ZNqj..ܸ/+/O@$-ImWI/_Wz5糂Tdy#2 y>\,z "k/$nRH8]rS!m1A303N?Ɔcs©(,Js7ZE-bqSFGk.>ѻkƤᱴub,t;q҂@B}x'OSռ)5̚0jQ0I#j*:կj;SVkiu{xb!(DrNxQpYPҙKxcmmCxDk?BZ] ڮ]xKIahϘ >qlQp_ʓp_ʼQxudo*3Ȭ0?8ՐOev:;y1yE6S֋);쿕B/f$q`[yHc*J+·O[l$FS?u=ox_R_Wn錄K83C HGѼ-rZdu9[K*ᰧ\Vu`bu WkΑeϡ@]..`p3Ϧ?旨Z8eӵv}*y 9Abg rhzqWAHIm"OӼTSR6rݵp@SޯojɠGMcgqyguq3Ka8%yQqX*M*|ˮSLQR{HH8gb=4郴x֨y֥7q ѡzYa/8*$Gk Tm+i@b$b=\(RP]N5ү<ٌuOEMf\I]tp{⽗7ulztkT7qm)\gpA HqY ie|~\۷ӶjIeCp#fBNs󮻞}9`;y]FT?Ӓduܸ8?.0}+ֵ/|$X#'V6V ^ ]shX=#RR4=MYme i^1Q-\ d[4WTs ;H$.=p)IQx_uw["jzY P, ' Ac֥m}u'kK+BZR2m.+KxcmmUfw]gѴˏYhϙC[>cBwW$[̌ hQPWS:bԖWq<Ϊy9Ծ 5ͧn^[o)bU7n;\V=Wp_ʍ1#.X$O&(a(se߆<=xZdR27nOúX]bSɇxE| <_,Fv`hm5 QlH|vg÷y>m-ʄܱɇxE| <_9;W{yvxЊmE[M]N0,9溯&(a1ϧcqA-,9cyki)-ďgDq]?y0Ns-4lD#Ba&)X.cӥ =X#ZL?/TD.Q v > +Q{[zuWWxEV\f]wĺkt;r>lp:פ:OGi4rrEѦ/Y#ynoim\'M(,YK]$|ؿ'O͋ߴ9B;gsYoc;v,$M ==a%Ԗz\5mnu^l_@? xOIe-iY]+ӜjzveiL yr~@?d_#[z5\=3) lu|?zޛY;0nWYE@?l_`\xsa4o o+ __SSˤi2VofRs%e#jؿ'O͋ߴ9B's]Gus[qEWrDž.y*McúzZo_Va HtP(>;⾛a-3&dfH+e+~)xӥ򚺰)9D} ^<~0ͦ pdS Hkw5oXK?C"N]a3IE.h%4PI@ 3EfQ@ 3IE.h%4PQ@ 3IE.h%4P4Q@^?:յP߻vOZtDu۽vĺ~(헱<`U듞;~u:/M"XgV פ OY5v["Lx$)9~x5KpaQ\X998>jPhmX}dB9#==-xji'OOudx2F}2zSNŴȶDE0E$3' hḵL!8ۻs!мUk^yV̀Ttk:\~ Yײ6miu z8tx gλI#9ybh w } ?3~Q۵V{E[o`dcbOˌZvP uic\܌f'uBL2rA3Ӝ3 sr~6>,K˩ah s53m+;I8Cf.OSM}%nA/~1˜т6]/?[aycg3A+E(JJmƕaI+W~FhM,bU6g"oJ禴.n8D,[,W=@2)i?eiZ[zQ工`'ϭ?ei@Q工`'ϭ?ei@Q工`'ϭ?ei@Q工`'ϭ?ei@Q工`'ϭ?ei@Q工`'ϭ?ei@Q工`'ϭ?ei@Q工`'ϭ?ei@Q工`'ϭ?ei@SR&F9|SI[/+WFNEmB8J՛#L8sN?j+:M[S^vXhu.?$e!"u{WŖGYC4%Hbp#L f>J+JXnlEMO,Zk-uU+k\F7qg,ǚw+2Iphb)(GݔuowK+bO]wmʜ秥uFĚeo71Cwa٦tG;Q9XWb dW-970R_2\QZBQEQKF(@P2 c<ޅ Wvw F'OzJxK}ךaUAh$MtB]]OA i{g.` {2.(Tdw*ayiZr]wJWvg[IK^(BJ)h((ZJ)h%Q@(݃'[_ :ʽduk@5J-Z41D&hv8]^纠4ݟ5;I# 0#fn]-@2yoߧp)w.6s+xGm-SI7Ps!88^>vǖWz+{cN/&%pF]L v Y,[)73ss{g''M9x9^)jFNO+8a*X[qC?YuӦP7VrZMHnr~ߚ\KW|&Uds,Zj$K>dPbB8w[=7w { 8/#UXk{Pa/,.-erR;qĆOk6lVOofJjK'2Nf*UaD8OI[/`'ϭG"zOtQV"zOt'ϭ@`'ϭG"zOtQV"zOt'ϭ@`'ϭG"zOtQV"zOt'ϭ@`'ϭG"zOtQV"zOt'ϭ@Ұ='}l:ҝ3=SIkd>iZ5:r,/0(Vo>[.>(x,MW.>'K5m^S.c͕8Gq^5fmt-&mBu_]Fzɯm[K{aJ*uwVnS*pwm2ŠŎ1p:HVZЩ}ՑI}gۙ.VRÂO(,+0o:,wI!` ZN)<]eW~^Gr\CirOi(b9ZVFTJjNMH#@Y$OA^SNj[>fqXnق-gi[85Շμ[ROR~$V%^K;IP&^  O1N#_:P;9皗:59Yj#<ĉM18r:V5+R bîk|y>]b/T[a-"ZE,P7uɮ_ƞ0RҴoo-|c S޺}7^᷋GPd#{#$.:U/x'J I=lF/~TZ4 [ziݬF\ӛQL o Gh)🅾p"_AdV=8$(vcDOLvGl{9 Gz"{xWպ}տ#FJU|m|`|:_ߥ67*xZa_ 4 Qmj J6Ju[&;:Oo&/HJ_s#*GZJMIOSgyQXwvZu6-[Ʋ,&A 3U{7fMzJTO]U(@sLaRwJNә#exnn-u.q \ߋ{_ X5ڑDđA8jDmjUI&v<)'~;QWq}jUeˇW{: [; #ENT3EVgtqTA^ ףj}b9@$\3c)Ҍ*_nÎ"tI_f)1I]Gh dukYWvεmԯRZQ D򪼝Ձ^c;@m6 [ pIՃ f7`}hiϩ[ܢIX$Znnao4ҍ.#&q o,gcIUv/J][9q1g]w\.rUf`>"?S7/::~wep%qRα<7k}6 hfq%r>M.Xh5e6M8ym`#F床 I6&;#Ui^~Gl99"h~ԩq,cE JY$hR'ZGway\Ʊ0ej6rz|  z"ݴ֑QJ\*^+|;GJZGbN>+%.|ArC73DW zNYUN$P@$ЃYa/h=;/#t_Ukns~5>-;Rbij[jU_ǧ]NiYuc:M N*@ s޼==6w$L}Ьy9 3[CQU~n}JxyW~⯉ GG𦅬5a@'9nFpqWS E-4خΖyndP+$gO>Ys>-m 1$I~=GJZgI#j[/}i +x&*d]s4e*!?SZ4g`VID]<>Qr` w|#O Is,2 C#{V+MIEY*J> x]Z}w×W#XDXf ^>Vwui4?^j0ƲKq5_ol>l>>_?eqFX??eq>_oymNJ̙E4-tk4X-\F85f+þ ⑴#"[/#v3}|wCie,ֶX\\LmDs[S'.ZO#.F#V)6{! \9{Ʋ&xJ[ekMV7ʪ0OIpخk%WmzN .Tg&K8ⶸE֣IyddF}9{W?j:gxo\:Fk&$s)`pvqgOy"};Zpg8z5)Riy7nzB^} 9̷ Msq3<`Yc(|uEm5;Au{3Y2xr.6If8U=''%nά6)Sn2W]Q߉:_-~8p$l),8lG3Pm$ p c>jKk40,锖88|i_<3FoK{$@=+IbaҧG~~;/ <4|΋U5{-kU gzW+p2YEs9$0 ~s<hx3o[+W{7aX$G y'h1nFGync[%7\OOB|wlhz[כ!3Q3Nk&>K.mm$r7u8=aMM;GQ; `q]&EX6?uQKʹY(QTfZݾkkv:|Qֹhp(C,i^5;HQ]T;jc ^2{{⋛]Mhq\X$NgaT0ѼWldҮ1:Mm;G>b^5__M#.{]vI n1irM76׶!"Iqi__mC꺧@ѝ-ފ('G[kdZYmѼ2;TVV%ON[m sWDk7X.&o#M)Ƚi=xkyc)x:pEVV\HIH1!^;6 B/dբW }isJz&:4 GO^;UY4 .73& g9_&ŒZIyu4-T2NymJ&n-mh;no-B>OI^x> hK cET9/t*Gy)~RS/>z^zx~tyמ˜(1*QWT?= OΏ=??:S/4__=_C= OΏ=??:R7J?Lh s_=м=1*Q cETWT?= OΏ=??:S/4__J9/ʇy3\VY|ghQq\)]|aQuiZh ?qN2߽ĉӢ;CK|0ꬾNӆ`8 WJ7mvU]*;yxuX{`ZSIK/9-(ҰҏD4 -(ҰҏD4 -)Щ&F9='}l 4] 9a (Vo>+J=I~tX&x܀c'cODI RKxV;+Z\´C+]IL?і1 >+c~Abzeֲhhi>!A[ʌ}v+^ sSvbj3|#+U _"LZG^ |tMFY^&2NAZ #^E]kK T @z$d~_Jڗ4xBylBV~{gv8ſwܓo~DYJ@tdnUxiA5E͞!#q]{)qV:2pO<~W[K GQYpV˰lrWY:r W4M[MQMi:2H=84[zuY Oq2F/nYA~ҹJ&I~t H&9`ȡpily[F:6*s\no.4<~׋(UM8߽v NyqBn$v@|Dwɋ)EThaZv&47viwbVh`N1tiJ4kFkSSS~2"i^o\n?ywG0FXGzKQX6qO7|p2ۜ⬯ZoZ>*_jz%-g,V<-VlutF4Vζ俘VVU!rA8=j֩Yoh ]CE:A<;xM쀖4 NWڔZkGkdc=i{ykB& <Ud5 BM'S淢rmyfMCη7j \񞣞X7}ڥ(=2<9V$?zKPz#qgJ;wxWRO/YV:IhJ3}{ƛWͥ}U-rn?\~UҎ= "0`0`r0zcY__Y` 61y]G X\-<\LҪV M0k/͸@ 2jvW{ΚqN9 ghSB+(PCQ6u. -N<<4l>)Lh?+z>^z|~tyמ˜ѿ*Q cFG=_C= OΏ=??:S7J?Lh?({ӢѦH]9vv[Ұ?='}l:skzQ+='}l:?s{kzP;V"zOt'ϭEڂ+}g u>&X??eq>_oloJ67`'ϭG"zOt(ޕ>_?eqޔ蔬9\"Ok[!JѬ!ӑ`|v@}<;c+es1#ױM?^?_mdTNPU ?$䊚Ѩxz-w=2Oҭ! y؋ ԦrMEN)EYq(c ( ( ( ( ( ( ( ZJ(hjJZ_mG7. ,IR>;^ O'jѽPWy=2 ǽz](a=WgQ|;n_h$Ot"ye+0lp8gu:ŝҖh6c5 x^WۻinT+G_iRi۽ <0+ZV\vbuОAqGƠǪܲ$0G?KC+$J,8U^%wXkkLvX2;+N.zvhuxXIu<5]ƣhfixǺn=RjZڤY@p;e_|E֬l)y ^/~OyDRs9PPa6-6v*SJ02LJo4 ;۱Frз:xZ> -o 23z s[Z[./H%!ʻ)e,IsRnp^GՅ|{gdxX\{]ݿpi&Qx&:*laqJ?η;L̣ui;ٛyI뵟VeӼ5G$F92d:Fc3SVse7yL2|mu9?QLԵK=:.k0ۡRwq^Wq ٠o>yGf}8R,5-»4ŵ;q3^)=,3FWӮ/ZQsYǮ@9wIO[+݆Z !99+SYO4ӝ&IżA⎶.Mgq9*$îG^EL7g+,4VOꉫFFVy$qҴP(;4: o<igk 47/HA]UåW;dm&㓟Jq3s\\1//8--'‡*%ݎ{n\E|A|`W!A,RXw͜aUY5G//IG|A#>tExQ56ϱc?ֿƏX_wajt|zz(,>uZ>c?ֿƾLO;?cEտn_Gl/ g^?TG^? P/,/k'^tS'ƿy K}ml__7oyLlzi ?ƋϢe?ƏY_^,?L'xq4X.{w,//5 'gͧ>"x8se?ֿƏ_(O[߅ [`m_Gl/כc?ֿƸtNo/T=a5 ,;X_?kkM{Vn_ jg X.t_o_G/"jzn__.ikjOto&§F\EihѰONHn_5:ZywE)ngls ?Ψ\mcGK1!~B?΀(RQ@ 3IE.h%4PQ@ 3IE.h%r ެ$dSymF9~&y-M 팮:I^U:jh4fGa*Sځt׿_I{>~%kچyqdmm62@ 4۟wYY\jw7r[yQybڛٿ=Z`z'uW8ftdRUvxS7;%>y\ӯzVIԴ̟  6wK 0%b7@4^Ơz q5cOKp$o$6> ҭ`Ӗh?{p!u߁4g^Ey#[4X,-.KIfwh)$`֑*ꗖM{iZRs YfRxLGg^/ʗ6r@&'VX.8_m}7 0d*Sخzvr&)6`%ԍF#PHM9@?Z~_+r8u)|soO ]FqwuW{Aq5x:Xe:"5 cNfNդ4&KxI|YCd'4z/uW}Aq5ɯ5}SPzg)[/bYFB$sֳ߉Wֲ2dLq݀gJ.`vF7|}*3F{kswqykgo˶0'+*@\W_J_c#ߦeiƾM[R{,-RO#04۩5}1cȑ l0Ny4J~!M5*!_ߦ{VKnجѴG\ڹ)~$ioiOsk-XLͷ$#465a~jU3c~kOkIGtǴmz}+\9@-OvׇoW|We~kkPgm5m-DBdm 9k ꣮*v7WEw"iH]~W$m.o=3kؘQӆ5樿*>jK1Xķ>!ҼOky.a1 1;5xZoeP7\!#rB֮E5teVKGE&P^w뽲6sd3Hz4gr@Xx ldiЧ2Я_u6j3sZ.VSvVcw?<x?k-C'OcWa0E\vrj*TT-)RiCvJQ~S'o?xTi {m:6xg",7g!\9C eK |ZӱQ<9t?4;/X:'3j]{?IW y?]N[kcg3K4b Z=#b3[sHsQtvS_*[9U^km̳iQHp#fk_H}p&?#P8%U"U2_v?Uz p.?U*_meu/O#k5 $W,qsؚҵ?_k@hl7hhl?hhl?hhl?hhl?hhl?hhl?hicq#Jߑ /Yi; gYb-Ӯn^/ 豭0b685wZ|78BL@;O < !(X]<>sq{x+YcvϾkC#vG#G??€(IK{8_K˳b rH;ִ}b(GwQlg=*vmRD=~PmBxnn'"K#;H'k~/]. ˍAQ`b2Q,`\YC`I~ȟ@?h:5Z*n8UT\b9-EUGD&=({H'P~QhM0۝OId{օԶ.a$lES۰G#G??­Xf,C0~@Qqj1[#ݏE95I0R$m<֦<#Yi21i4[cԛE('X`$Hߒ]rNNknbHL`~RJ}Zez+p®&BQq@?ZА'#TͿ@x{b[[_9.<ҳ0Ğ[gޢYebC(n(O6Qoe?e B!@T'I3u*,H 9#ߊ JѤ-/5Fxs^VO7h'4stA% FK>gWC=iv$!4ޠw)n,L 2@`品6olѿK3cl~k1nb5C~ A cpKM[,zܨv<ʽ[u#HVC.+}Eyċåú ѤSGQĕ8U.0,${άʍUT%.YxzVwλgYGκ(m۾0I%\VEntmɢ"14M`)<3:c!8*q[;szw=%nF6,vyx9WtxK]6kxj0`:?\9?/n'Syíx^TOZi<U ؝̌?ޕ+?8Ffv)3+gWdsrq\=bY'4ؚ41S?4l?hzB?Ω?j"y=fotoxx-15.11.1/data/images/color_fringes.jpg0000644000175000017500000004415612616075370017403 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' 454 292 C   (1#%(:3=<9387@H\N@DWE78PmQW_bghg>Mqypdx\egcC//cB8Bcccccccccccccccccccccccccccccccccccccccccccccccccc$" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Ѵ2MOy47v $'l@`M 'פ_-#+GC9#zC.)H;t8?S]Fa;ϛ:T(*?r8=;[[Ei E''>VM'G6M7OD@YRN* _CtT$xfU-Y"D*R7U{'Vhjd6T;!o1*CI7??ʄO߄h@{M.hoȌcҿc~O4fȃ4߄h&@{M4r aM'??Oi_ њ3C}OK~-ÚOb4߄h&@kMBt-9oTʷA[6wSRjrf}J5O&_ m{H -ֆd&EG$` ҴB( M( ?? l յ33 F=Zٖkûp1!y~˥'I]+??,'b>cL̸'k EynW6@kMfҿ c~x|)B>|T,"kU|Vaݙf c~O5l=G$Lb@29y&FhM,c}Loi? m'??ni3WO;}+??>ϥ'MwX߄h>@kM34fgsծ\nE?6@Oj J.AkTTcgh/Tu",VG=Q}sV}O Q%};կ7V+/ 5@}.٥aS{=Ϯz榴7.pY =3U[|1n>V_j$աH$:"%H}/n>V_j7V+/ 5@HWw$$3h.OVFSuoT&TFc3Fj_"_'Ⱦuohi3Sy?E6O7O d957}@/ _:dt{D5}@/ QQ"+n#zҋ[tFpl{Yln:dt)Eh2c.| 5G,̍ ""<6sGe>}@/ r|is4 n,A9igu=6O7Gyk_)Hԕr~~/8;Q`pI)nqR%$I<>L95VSч<k k[mRx" -p='zק#\jZ[b"t N@$cgjڄy/!&qSvsKaP]ElaiiW{}2]lLqIv`6c!v}ođ;xH#C;2vHĝѹErgJ7͟_fe"-ێc~ze mH B}\}M^>Y *6\rF 6Ǻ?1\{WHeDZXUpK`~h3UUծyonm%6bh6 9l%+J diAFJ k$G#FZ l"/RDus)\9Br}1T53WWij f#>`ml|AgW|>3'\c>-sE_1@R7}4^Em~ ? mK[@7/:{ŭK4i|hhc#5X[%Z۴E]_,~`iSY^amYڋ_x_ʊ+fhcl6dTQEQEQEQEU g<~k?}[c/4QF1\(w~3a;?Hp EWydVFSG-RE+;!?3EvV?&zo~i #g]7A?B( $X = |qG"(0y! TOڭ??o39ٿ?.nk/J#>̞VN2q޺&d,FXKy9qȿB9X;{KikQ#~  d m$}QrjK$#NWKg(qȿ_KSdRy ##,j`t7QEHŠ(((((((((((k?}[CY(G%pc1@3I+AGVE4hWO rm @?!J/;4w┦qVfOs\G{wnӅ,L H ];kuc 3*$XrL M}9hG.rI[倱Fy=ARENe9 _Lb|hS}z)*7X" B;ԔP{$wWPyUUڠ''񧲫V R<=ma7mh"ե~æqT-1ۙUGkN`Cik [۠H55P ((yG_V9*P?@?p_hc/4Pei #g2YHB灓Ԋi #g6:`ye4hg 2ZsyQ{NQZm"֨ luѨ}[$[] qԃޕRFD/5%ԔƲBT#3M _o,9b;Htʭqq *)ƒ`6'"Z .ӓT @U4--oYQeu99 N2;Re[!.2IVA\GC,˧,jӧG3,zs#\F;iѢ(ˍi l6L VrgR70jl$b3h$);~S5" Sh.Dq8=*"T"Ae *!im*j':g [%XYW!@M_`g[{d8`*j4 EpnIRku#r] U g<~k?}[c/4QF1\(w~3a;?HfY)D3 }hQMwXлUQp1USX\19iI7,lrp;)1Ɗ2Yj)/HK%6iEEqq>/;ZK{kfm l}q@QPaUȅ*9-m?4o'B%7aP* ˉȌܶ=qmmamxm]l}ph^{KuVH,>2ƀEW60wnFă3uqv!7e˓3pF@.QL̖Hz+Uhh%UvJ*q؏J@\rD,rcc' =F@-Q cXFA=qEwu8հW$}(U dI/ ,_$'מ*H,didFp45\XLFDimz9L˃Ʃ ;y&[$ݘ2pA_yG_V9*adhG ][Y(G%pc1@3I]_K*ʑ|ap@9#$i #gC"WF20SAS\Yұ$HWqߎաE6$s-o47.'wg6XEK*tW`I Ytqdr6r}y8EGpάHT,!k\ )}N©jĒ Z3kI)3G< mBq'B01U?t .&.19?"G=p~E;mGA?ɿ7Wl!ХT9֩b8ޥsr]Jr? 3/"\`G*,FKc#Үi-, n ?|oƮOϔU1ڨy\yOnbӵ{zձⱷS5V>sQOϔU%awߒ3*&brYDg g<}M$Ύ-eQ哹_$R- YOL<!MuM:VufQ,BÓRĞ\1ǜPEl#wK+#/srG!ŌAcxYnЀFz{ջH-.`'y\FX`z;iOϔU1BVR,qMаe$.?;EDv3LΞbb0HH'joI>SQT}OhH1T$K\%<{Jyf"Llj 6\e2^%%1 IԮT0Znv3pH/uʮHR'ԁސCerqy[G|ۘقCZY{ki^Hexl p1 <kϔUl|⩈ʚs:-v+6\Һ ??*I>SQTtu-:]kKo6(\Cg}$)xџbRdc%K6bb0xv5QտJyR 9 U}g<|Cf>!?3EvV?&zܬ;Oxg={cfy\rA\3Ir_>]ĭ E-F;`nfIAԚZG8wH5m¤ma^Wfz9ڝ=Wek9< o~C8ؚb/~-5[ߣRj؟"L,q+`,EVtLȤ~07N #'҅ ;ؔ3Ox {ޤ1$CH_+S⧾P?O?ne!.F:`{t&b_Ѷt<٦nQ fg'~:"ZRͨ5[ߣQkGb #ԭ甲&$F 7$g$I.3sڻit}(OJkGo~V#:3>=D$Mj&m9 >Z::\kGG5SIu b@揹3޴!#{;eΫvFx'@W͎o*VWʖVQ*QտJ"%'1SMW?@?p_hc/4Pei #g[+[}$aeZ;?Hr)"KG" 2#ЊuV% Ŵ2da3ґl[\bFZE2X6hHaA+HRXcTm0lc#S@KqZ0Xc=iA;C q\uh/Qe˥]I6i*~݂!f8P2M@.` Y2AF3@I4#جvRϥ,3i0+2YF˄dMM\~iQyt"HɣڧвUs>WT}|?4u%,n 9_5VYHۉWݰuGa*ຜN@,uGSO -a5{P[Ih^* JLF$pzj! j9f(Sɀ3HMUcTmtefb*LvT?i+?>s>WT4)l2 ˇ>#Xḵs>WT}|?4o/>b ©8{9\_HG1m\PH%z^'s>iPmN+ ɕ>hRX$4x+1'#?Mb8 %Aq,ToiGs}yt9R4iΑ"BNM\~iQ ֚Uجr{qI Ɨ;C5e\(}N:>s>WT}|?4[?dY.nrK#>Ws>WTO$jm)Vym2;LQW9\Ң?WVw)"4&/D{'He۟9LUZuxm\=[߷ =y%Bcg2,lK@PZǜa*,d1~@u Y^S s^T5xys$d5/8ϊ_]*T=)؊Lfi`]eh]8:S.ZZy]Qr $i]A! PApybC,rK+ 89g-R&Egvf.1qB%b]ƢDG|~ߞmǸb):s1>|M ᔂ ?ZK_ O\S-9*m!Y6C4b~[A*I-5&e.'T#mA vv ΈW TiZKf܂4AA!Ht$S0eۻ+Ќv+TuI96.ޟ)ÑZDc#?,Es⢲b}$rсCv2mI18:0Q60A.TQ%"]Ʊ8@o~qdIU&k(>d'nH2gTĵL!,i_|saqڐ|?OGq@[I y IuP[ |Fd6i-l}v.Hی%ctF#V6uQp󸌎o(0E> %z|41pHWBy *?OoU+{ؐe]@ޙy^CfʦYƠQL ~?[U,k"e`u?d *tk[bu*I.O[Hv1&F€rϡuAh6x,V?VlgW=2jQտJ>!?3Ec;+Oxg=Zm.I|21wUm?៤LHdV-P)%b@6= <`NB8E7jcK14qٔ\0;:Yc^K#&r8+Rd`V(v*ʺ}&k; %3c/ɜqs[Rt9MRl'?Z{~F?]Vnli"NN8Fmk r"@b߃Ѻ{~@ s-;T啕+[y|N\I"UJBG\)<qX?><!Ia >_%Ud?ʥU0TQ4"1m0w-,I--֑qoPGL~5,z,r)H͟3*#Jן7^x?[i#o$2ʯ' 3N դH tvu.ןI}Gyn"v $6;e[c7؃jo?*m-$ RSj(#vsc'MhOC G+]d:C+i\raha?<|^9ӯ-uN3{~F?]7kX`S-Ry=u..gl"8u.hTB{qA 1 lsP?}[QBQ˻lm] ǔo81G?p_hAGZ7{~s  \3YV?&zWEPd mlgjC5Օ:0eaA"[GIV#q !=X@F `IR]1nGA>6oo/#w^Z |g7~]kԦn#K(F!0}GzѺ''2E""89mFHϯ@1Ɗ2Yj)/"9d#;HoR\:2 drɦʼj3%BHC:3حkq98̫?LowmuMἷ *q-͊K;XD%|6қXED<&2'P@֟[ w!iW"䷱XxѼ ܔ}GK:7b-2J45?t}?Ԇ;w~+MhOgX.R ov vin/亟-+6* )`39 l9؜|ï<~{KuVH,>2ƨjEq=ŋsN\d +V79NdHXT60wnFă3uqv!7e˓3pFm]qm -#=ҫެm' eFrqJ2[!UcH%E>qTQV-?/ Jo!+4'y0 LפE!9;XJ-sav~3}c[۲sutH݇\8#8j;x_f@I&}1zM>$gYd\0~tXY ȍђ?i{)8Xl傲Tʝ"A;?&gO1v$`c$ b\XCjAx,ilR[q\HTNx'}Gס&Z!.@ #PXO\`Qybc5l#$J}%1 E?62(cY1crd!?3EvV?&zܬ;Oxg=nPEPEPEPEPw1yŜoR9.TU{9KUXRO( %#!*57goN?l|d?Q}~-5[ߣPl|d?Q}~-5[ߣPfigGrܯ^)bYI,'N ?˼Y'Y"=kޜs˹FCPp~c$&b_Ѷt<٦nQ fg'~&d?G$)o~Gٯ*%d?G$)o~Gٯ*%d?T9>RZ3UPsTfd~s[E4P$ ~h;,7P* oW?V͎o*VWʖVQ*QտJ>!?3Ec;+Oxg=I,0ù,\oR?៤LcFur]rg)D"E9rſS,QNH#-+w+X!osLg>eū]b33dݸO~a+甸?(_,ʔsǵm5@5$9XcjaphD'<̓v=3ք!&,uF*9$3iYBXmSI޺Ibx9Y#aW*ج"+n"]qar[\%2O${q/ɴܻ399ڬh3=BY֢f)#孑kn.~ #nD6@fC(n>iQuroFYA*I*2K69Prq׏Zu4S !{Ytc[i&ѐ%O4жo1[tc:m2x #BDVdķ*} ] L&Na> 4{eubW (o:]fixR)D`])Lt5c#co$b ©8{9\_HG0z8#2t=EHnJ`~-Kjw _^L@m$ġYA9i NγZzuyH)Iw$2Fr:qոҢH "]bpe;}g<-_'i8+TZyG_V9(# |Cf?៤LXv?&z{,W)o C#׵!S"q$Hᕃ(;qU;2;K8%Pc:n̖>|/nU#m2gL>%qG T׀rN(V7o!d\+07gGO 4/5J玽@VmŶ%hfl`˴`gi<}fO%B*#y?]ƀ4!f8P2M@.` Y2AF3I:77[40ۄ430zI7?qVYxĘfiZדLɵvN~:Ӷ/nO*ϕUh@s Ci8b 3SiN?5%ìw3U?$uJj)':n<! j9f(Sɀ3N;SlDAtgB|#M\~iQ46,ͱB8zjҦb Bΰ@t`'M\~iQ(5\PH%z^'s>iT׿v'\#a}~yb8 %Aq,T %).rǠԆi+?>s>WUj3G=H$Dh_R{F\.e% `cN"M\~iQ$եMH<`#cn$n9`1ש:\ ϕU,w[JU ^AkB2mI18:ZIYܤcU=g<ed\BD!8fN*QտJ>!?3Ec;+Oxg=i^XӤYax񝧨jʹw~3!–vFW>bdi"dY^"z:c#~)#:]!$滮'*GP:bt#&Wk3L:U(!&E$czc#GY:+!}t#b=)؊Ӣ2iiQ.4!Oi`:(>=ֺUܗ6GȁD 0;矠S_QOm[R'2)"*8=*OVq\Tt(~Xs^*-6+'ޒHA i>e߃w9٩wXq 4=Nʴ[uIaOb1cgQٗ;6Ráah?)ei/N&%%C R@ ~G[Ty *mBןh/& Y 3c8-l#PD P~?<0n?Z31r!`X<5QտJoZ ,3UrPF1\(# Z;?Hr?៤L@Q@Q@Q@Q@ح)OuoaQw-% QLs\Su>[hVe1 GO֐1IʁJ2EU} PH n?y!+em;01rYί(ypJc_ *֫= ]؟_ʝE!doO2\%ݼk$FJ݀n8ߡ#zUKY^YY^hұbZNII?m[Sq3 T!SZǝr_*Z]rΡgi!?3EvV?&zܬ;Oxg=nPEPEPEPEPeeqaBDZO۹ 6Jy2K[8"Gt/ >܁@n߃Ѻ{~Ge?E ̒8p,vCK{~F?]p~?F\I"UJBG\)<qX?Mhؐfn;;& 3yrpFP =EsAA2*3=pC |dCX" Wsu42%(#ן7^x?XY ȍђ?i\ ̂b2#Kn#0%y=u.?qh˃ƀ*Lxq7S|T\XCjAx,ilR[q\HTNx'~Rqc1@N'#w.Gn2?, =ńo^_گwX[rI=Ҁ&y=u.vuI 8K;O,mZ{" p ܲrh]ן7^x?̷.7^x?5A:aT(G ˃Ʃ ;y&[$ݘ2pAZ]{ch~gU]g<f02\R\et#O꭬ǔo81G?p_hAG[i #g((((:7R`$'O}GQ??*谻io^glw19,33x>SigGrܯ^)bYI,'N MivRD#Vm[Tdwtˆ8>[DxvedesHzY{ki^Hexl p1 <RkiάlLrwc gJd?G$)}@ETd?G$)u{*^k'rs<cR,[Iޭ`Q!pы@l@=ڀ$$ 3?i!7!N 0["qv݌) Ɲc2RF w,3C2;8ZߘwBsOC%Ȋ{w]8V$)>'O}GSzhIcco2$j}槪l|d?Cww[t֖mQ+D.(9U߶I>SQUM#E%>^=}EK30la j g<b% @$rAyG_V9(# |Cf?៤L{˰iI98ƒY?&zr$:$3|/#CEZv[\8J1Py:VqpM4˩'H2{` U%W17Swcic _mFl,吸R?s[BqjxYǘ?M`?i}&/UGZG pC}?>֟h(=EPkO4}ϵz7QT>?M`?i}&/UGZG Aj ,b>'h–$94;cnұH8P3uN=*}qmù ^M_ 69 c}{yo}G-vx &N0X{g?Cf>!?3EvV?&zܬ;Oxg=ma?:nц?)oF῾?o@?a?:nц?$o'Gح/ToFiSp^7נQM|z0^E7 ||zu7ף |Sp^7נQM|z0^E7 ||zu7ף |Sp^7נQM|z0^T5rU7ת:"IR 8*zRvXdc?HK1$bIX[YJ*?)*Nerv1,z&}%t/U-g~\[L#)V_yE=ϤTn}%4R97RU7%mR"Y]t4IKWX]*,*Ya}0fv | z1?}%t/U\ {I?K}'W3Uu;X!pFq?4JC'݃ȚǔooR?@?p_hc/4Pei #gôw~3QEQEQEQEQE'ͪJ,7N0UtP ƀ9yX?V2e 8IR}Nn/mdR ske}{}~W";̒" ezNjk2^f?2'JnBm#c5{}>o=V1E_$FV\HPLVG77'lbyu>(ǔͿPD 6#&f<9օҗTdMm̋X(s&{I΋f`6=#>)Ƨawm7~=ke}{}rev֚-vWd? St-Z;O'\%Gq8/Q(=E'7%mR[$C6* zPN >1@Fb`ix[ 08j(I)Vl@I)tFTVŤ'әlԪ+~oʬ OtKTǮI޺_[xG-_ا~/#wzh }hc.܍ۻK2Z"- L8%XJ>o=lbp\Hу+H#[Y(G%JB"u}9֢?@?p_hc/4Pei #gm'rJFw?E??aϝ;4Q@,;ƏXvsP |Eð;wha>w?E??aϝ;4Q@,;ƏXvsP |Eð;wha>w?E??aϝ;4Q@,;ƏXvsP |Eð;wha>w?E??aϝ;4Q@,;ƏXvsP%^kTpF9}hfotoxx-15.11.1/data/images/batch-metadata.jpg0000664000175000017500000005050412616075370017403 0ustar micomicoJFIFHHExifMM*JR(iZHH0230s0100Fotoxx:trim_rotate|paint_clone|zonal-flatten|tonemap| Fotoxx:trim_rotate|paint_clone|zonal-flatten|tonemap|resize|http://ns.adobe.com/xap/1.0/ 600 1000 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((1" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&62 9\=jԷVb fJ\њdZ,* _$].=ROa<њnJi*Jn ٌbhYI~.l_;Jr\B{\,TruJ&O(X3W ʯ)|"95j;gHQicEHf|.(>?)|"95tOE(?(X3VYby"%s#<*"K4|_QE sFj}?/(X3Wȣϋ|.(>/("95{Ϗ|<Ȣbh^">?/( sFjW|_P)f>?("Y5wϏ|<"yyWF[z(V=p1tQSڤb0a=qLDT_iI)8hTFSR+ܫEN+BFϏ[yتq`? z*w[iB!KFGPA~.+Vuţ_ZabkZ?wŠ,UFYPzSqh h?<-bdZ?wŠ,V1FYTzqZZ3V?wţZAbhXŠ,WFc+Ռţ1qh EXZDP#=Fx+ (SGVY!WS?B+mFѭ4ma^.pqGNR3G꺈&c 1@ s^Ӧk[k 'LF}xMr JG#aʣ;W6v4(ƟNo<ѽ-yw-NY\1 _LBe9n=51tjXuD~a~1moO)^{JCmS$t\,e|N/[0H~xgXukzݯEKkI-o"Ixd7~jA<6uQλ$ ڐ?|Kj>ogdKYc6nXt]^hZDI$Q Bֵ-]4dpt,6.uZDK$E8Rz j'G^[ Li**S`rw=hXhk;֏Z.4|<@֋6$VgY<0hE&h  F;jq* ĨpȄP2D~Bbq?!Pe[ un@ 5OcN.!Ym剹W@E lO'(؟OT6=Cmj[YT 25?V>6q6'lO')iTZ+[G>E<2C+ѼQi}It1q`͕fJ-|Esqt'$VQ@"c9> ֿt׃E*/Zܷ*%uL2jxlY!HeY0AU5x.HRQaVeG$r1LWQN:?l//tQ`S6}bhͲXE ?l//tQ`O2}bh̲XE ,//Q`O2}bh̲XE ,//Q`O2}bh̲XE+ٖ_Ge|(\e>5V#$jE͢EO£y\I)ב}hAXhhIeK{2'(m>d`jŞC-Yf/ynV0\dn8wЦ%`%Y@a"qrJ`}jKϴZ,Vyv\lH*sֽ$PNj)Yφ%__[YKK#+7Iaq5"jMo‹5eRBzvh!a[f&uYUBi27c~Br[.uLmlnWl ^(P]Z?L߅zFi3@/E,>#%IV?e]OQ[4P!sE%RQ@ Fi(4QEQEQEQEZ@uҳ<'R.;,"TNz֓})g!k[9\yRK=.gM qt9뛭H|=-naqր bXk6zVAe6nePYP' e;qӷZwNkd-17~DGe]$.OaK@2l92]Zheoֲ%-2 '#A8_Wvy=β:1I^RɎp緭u6df8*Lc#5tFi ګ6 ~TFe2]BM)|XiYQʼn!}Fj6^ 'UWNw>8fCq n5 ~lzZuޝ4:E_%LD,U/ zcL= tVM=ab:U]dY^]^[h-XёSxv(h jWq֛]dxldKqx$ם=wA/}g20B>[iG#H:=٬;99s+7++#AN)m4כ.ўuQߋ4-Fݴ;tB`eaeo2o]KIQ  4i~6I72,`öѷ'֯%j:M!e6R{0 1s5棨(󙅬ls|k/@ J{ZyB(zf⋻'L6hK,q>b-x;z+1j3mx*^tig-ZDƭ.GXu^^^ɦܓ#$Q3+*2ͱ>-c6ji Ɵ\_i$1GleW w +;MESƫ{?*݄’ċ$Ҿ*})$ZR)(4fJ(h@E-PIE-PEPL>D93z>[\\=/h%0#[#jQDO׌?1/ Ie}FLY/.nGXÕr@\89R_|=ӯ,3oh-sA>Z)/a!;mm~]h]UųhcM̪%܏p#u(QɥbˤI~(4Vf5ڋ t|` r*݇/u});)av[ \zus5ƣmٛU.ϙ39,C pj⋨5H-(%O!Fufgƀz&kuIpf-.f>-,Yo3#v4X4f)(sE%QEQEQEQER)O9o-@B9A2H]?sEzE-q'!l_(vnOsc$[7c8푬s]F&Ap9ʀ7)@q7~'{&x1#L-S$1n$8gӊúpTTn-@;gy(W ?5)(L q&pN tӭ/*i# 8Q2Q@Q@G?Z@tM>-'HXV$i,B ~/J)QEQEQEQEQEQEQEQEQEQEQEQEV~?hV~?fQEP]n{\4wM41̪uV Nۇ\:\:ؽ ?hu_Vi{- -DJGe#;UU"ׯu$n-0eF`s❂ZdSEI m ʇkz85pkZUh$N Q"aG#JE'>DR"1uVV=HEcV֦hy`@#I!}5`у@y#hQ;ʞ[ dUKpi1# t9c @ZLZK7?m$Xϖ@qWYF `ڧZv@֏;UQ[4`(jj2KMo`Ҩ9 -.#28 f'ycD]ހ&:}m@e>3^v@֏;UQPxR lH' (,TzeN+#j ?k{uCҎ?oͱSm%Czt, --,tXa  v_R'] SqWQȍLS YU+6Vv :KRN2 I7+2_ P7Dp=Y)NnherŠ$ b fZ#`0_Wwpv`qU׼?-L[ٯMܗ#VWz#2>Q5gTt'šu}G2 Ib3߭mlcTTesV<V6W1kpL弭wx7Mh2IM@@ln6DĒWH2<=5SI3Odx+zRg*Vd>R39, tMaw@t[+klcTTgҍyZʁtyZʁt\ϕ7G7@66W3k*kF*coYu=B(+6elfyr~hQW Z0hO"?C]qWFt}(@W?g/Q Mmm u 6u&(66Z(66Z(67?֩;?ր-EQEQEQEQEZ@tQE 'i [S=v? ?GUGv@6z^^t66 ovRx :Gauwiڌ#S``fŎYGs'5 T|QH;hhZD빜){Xҵ]R mw-&y`x@CEy 1D#*?:vP84`(@уSleCF*mlphQ!Fe&AK&(Tk'JCC vEΥ7$2=)X7ZtGxpJ` 9 /@ejQb,aGzU~J$>oڳ𭝮ڢpӴJP.q8x c;8U"78m$wżmX%%<0!Sר緒R)cy8$pFdFJzխG–RL ЮPIB2g'桛^ė׬eM* 2qҎohldX4`Ի(@`уReEF K 5.6PX4`Ի(@uEݥ^ʨΝmijM*7]6zQR`1VԢ ~Ck2P.W9 $sBj;>;uV@$@y?K/d D/Gk[Կm{~\_|)ibW& >drvO/d KFA⫋lQ(2H8vO|oڏ^ߓGڏ^ߓ@9'ij?y~M'ڿݓ5b:'V9d1 (Ti.~(?ܓG?ܓYtP#SRIRI(SRIRI1@ڐrOڐrOeP?ܓG?ܓYx$k뤸D9Q@Q@ ~=Oqi -?]gK:{,K=cy' Pufk}DpaۃUDͧܥm&>pBzg4=cy' >Ǭ(e?R(kViX"74gӽQ7[yn嵹Ir2.x9ph2 >Ǭ(e?S-T&`k3`b\1z 1BdtBPg >Ǭ(e?WE1@ >Ǭ$](sc?2)>Ǭ(e?WG1@2(<StxQsc?zOOP9=cy Fհwڤ2bLR14mnѼGXgsbG(󛖇swϥ\o5P:}q*Mz5ċ{j[ ,2)~Ǭ$YF]iqes-ܲ#ň857Qmc &'d o0B20y杺 2)>Ǭ$S#R D,)l}XI)?e?WE1@2(<StXXi)?e?WE1@2(<StXXi)?e?WE\P9=cy' >Ǭ$](r1ZDg,G1[ǗhV$e7#:boTa9ӭ_H((| f#r-~ۆ۫e()2Q|ZuRXIϝYppNOW M1es9<dǠN>da2MW]LҒ2\/97_jd >ϋ.Č<}ia{OECHL5hz%t.-.-&y pHw|;|` $IfYQi3Fg<:.I2G@bA=01أB"lWY8dҹKo D]!yC9;G=V-&m.H^Ie>P!T19-9e'IAfpQHYݰI҇SNWv! ␌0#ȩ["/&WrL}xٟJ{xHSxīp8XIqwgIC4vH H8k?G֚u]58>cry#Uv/X.,~ҦPN3Ke4wSDT $s{f}ޟ^43Zix$$FAbtfh[P3Ģyc$H3Q]}Qvf`A$?QEQEQEQEQEQEQEK¤𺶠6JR//ŵ2irnO {kBn̞ y#zF{E6,KI#Amg<4Ws?K~g⩁<^WIon%hi%Z!ةg𽤗h{.>ׂHr) zU/W?G^ ݼ'i:BK{+I. 2 ,"Ӵ[+u K`1\^ 3UPSfj߷?_LUu;(\^ /UPSe߷?5_LUu;(\f 3UPSfj߷?5_LUu;)Bb_W?Mm/Tܖڀ.a'lFiq2;eFiS֥DݡvF;By,mm#sdgatҐ[%56͋F T& kvGHX:J4W?Gf "_ 2Xkީs60)({e1ֺMe߷OW?@N6W-~go3Terٚ&*W?@N6W-~go3Terٚ&*W?@N6W-~go3Tl\^ /UPDzU1Vju=B+6#swimu_3bgfQ+Zf :~TR Z}’,e%xfIA:b*pi|ƝP7OlޥʊHWT} oʬyz͓rH+*Fӯ%F}C8*B198>lޣ͓ߥ=-"*FmVomceXARXC"yz͓Dܯ Q oʬyz6OP_Toʬz6OPm,d>% U~P /ҙk31h( ( ( ( ( ( (%gFu3c?JeM^gXA^մŧJK4($cǭXd1"97Ct._`J}^ tW>nVQ,NZKr]\F`Gt5KQBҤSqwt\*{~g;IΝ$7b )c*heX?23C+]؜oC5aFd:PGͷ\!foZڤ$R-u&2-t7I8]ü2r\GP*S'4[e: Fr.1޺{? 6["{ak"$2 1ޢvP+4ȑhmShKM{&C`*Gҭ/Zٸ.ۙO\CQe(QFmjmlvѶFm*5.\smO.hD L!5TdlUk vaO0a@.O4?мo-WwIuiIm&>Xـ1Fq]6!dhЗ'p1;իOmI.0Q"bԦfsT#>+ԇ㵅my JduM1Op+NŨV\ZUK_sZMG{O9K}7I+h#?羛~$bMF sغğrMC{O9] sPoQ}7I+sPoQ}7I+h?羛~$bjM6Ѷ9MC{O9G&='mh?羛~$aM6ъ粿ak!Ӯna0%jH#u#oS\w17k4AKH)h %)(((((((((((((( `X~2HRGUiV6@8*Ye4i":kmeagϕ_ϲ+_L ͔l?/+gϕ_ ͔l?+_k~W)\ ݔl//+gϕ_neagϕ_>|07vQQe>V€7vQQe>V€7vQQe>V‹F ;'e>VadE>% .ldd;[*áUr?5gܺRRTp\8pE5R (X__k~W(ϲ+_N7vQQe>V•Fϲ+_G}Zߕ .6V}Zߕ ?Qp7vQQe>V‹e>V+\ ݔl?/+gϕ_nleagϕ_>|4Piw[PZ6UIQR#UAZZۡ6H?ʝsր+ZAK@ IEQEQEQEQEQEQEQEQEQEQEQEQEQEQE,ҥe mEͲQ|/^E3l?z6=Gק@ /^Q|Smz⍲Q|Smz⍲Q|S=GF*J(=Q|/^#/^Q|J(=Q|/^#/^Q|JJfmzO_?emzK_?x-C*A Mś")64M_?6K=ץ+K]q%Ѳ_?;7?i }w,_?%,eyJdVd`:d-de%_qlpOkR^K=Gץ/^ }3dlzOZ>sA~V_?6"%ZKlO$[asV-lsvı#j8;>ۻAQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE,$ 2N8U {U(.[ޟoO EozxQ?*?(ޟR?(ޟR?(ޟR?(ޟRrzxUJ( ªQ@\(޿RF𪔴[U.Y޿o0d@;2QQ@\>a>Q;bJ\|/?UF BqM9r|y&?z)H,.QM?}m ߱}O[o*:()k|0R\ jEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEfotoxx-15.11.1/data/images/mosaic2.jpg0000644000175000017500000013413212616075370016077 0ustar micomicoJFIFHHExifMM*JR(iZHH0230I0100Fotoxx:mashup|trim_rotate|trim_rotate| Fotoxx:paste|tonemap|trim_rotate|http://ns.adobe.com/xap/1.0/ 540 1250 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?q1?F _}H48 H',;bN~__Zao.=N+w$u_8efA$?CQN0xzxBJi摎vri6:.)so㧵Gwt"W9%e+?O\% {;F3YB$H@}y+njd21 [vb9e _9V|{6fBn$`vK1Oo& >v_Ν)2||9nq҇Q"ZBI$ Wym`㞿#t+J9P:cۈUw"`m_aW $\R U9cr-;'x=N J|?Lfp| ӭ`R {&O{]JXH1I;Ǧh^dW#lqO(NRg]ܵkcv/AuDWB⩒Ci %$'}?ؓj$GsɲF Eb~oc2٧xr5Gi-rCI՜n^ޒp̏v+$*58WTWz3 u(xK>PXFOB{#%qꦙr cDʯCޝ.aڕo_m|ASɮ][l` 3L(ck ?Gɶ]N1qzE9/fU})Nd|'>Sv{e^cM"  ֳJe݁ڡ+Ǧp*\"C:[].1?<ԲlͿlO˴s gft'5Zjl"Ib-dݸu>8ᕤFO_0{\,DX@sFIu.QU'*+BKfZsm*7##Jk0TxׯZƇ%?cLR }3S&j Vnn\dSmW"SȂ{{ZѶu4ǑoUIjs<\򒻽F3ʳ]WReRD;sOz|Vv!"cG֠:妙l,hAcq,),C rx{sVGVpPe"Yheq&yw5\Q;$2 1ڵ.&~ v,F]9'0 ܐw)v-V=CǹG?ŽIƬZ0Z %+U_xq33C1i OT9ԩEd.'SZÞllgB'#e=F1R_xGBDS4a&E唊SVCn!` #O[ME̋ .;֣noA;ggZx{?_0V;3+fDžcdzsKsOnԈ ңpn~ꭩ,4SB~VٜnS5ǕeɈl%S= C#d;Ҟ[ e`#kEy'ʘ UR2!HSUcPݝ|g4+n4!tkrNόJ(WaO/QP B6w 7Q,`F{3֥a9*#gU$0$oW;1|1>KLzc8CJ4%6z9,8݌4:jkpiz@4['A`Hq'->ory$qހ!Nz=~dqZQ$Lxd??P (99=踑rT;♐ ڀ$32țK79;OOo78'y>5xe9O׊U{{XjvwPHvİLĤcEysABXso!8[4G׌W%J-kXu:_-L+֧P[OSiuDiZ6FRwIoc\2$DBW0>n?W[?\ +*$Һ߇;" &ѹ?]/!Kc.'zS9 z nG{c֕a99zFvl8\ Lo@x'@g&GOMp qp$ӵ4y L8c?.4g SS+CjrN};U}1<֘Ē[=( :ӆՍK1Iy =@I\1a6֘[Q@8gcjPsRs@D)?8 LiphP367tfwnۜހ,8IpņF;Re,T-E<>@)!cq5<ɎGZD͗2UCɂvI5b$*G Q" '#3*"I-aRȡMdUcRyF3h4)l g$vhi rh=a3$Xtk5 x"#Ea9rEZ&[Hn 4բrozEH<g8MGռB5w˖5R]Au"+O8e;{M"; 7^i9v=*(O\i1$6I}:U;k%x(}O.5H)hz~6ޒFVh%Ӯl݊]!nN=iÈYNSwL{r(Ŗ)]S֢Ϝ3$C(ϡ-Ӯ +'ך~t,NH,zxǠb%wcyaI gCT = Ym_͹WLqpƥ\{\併+ǥ&2d 8RaW8OT!Yjpy݌JܶѮ ӭb;oς}\dcGtPfP%O*m y;ϠK.-2ۘA$gκt|=qb L q泟D64RJ*q};VZce5s4i'2PgkC$QnH䓏0޶Xs@~bX)^9" Ԯ)M#nSbc Tmvr9Y%X6[Hnp3ުy LWSPْ&߂(-d̀}w |ɷ0S 1߭X Y*z bsϭVxv w@B$8=0~yˍ7q>><* d1n%;OOZC&qTvPGb#}` $dc4 bwg;BKdZ7`w<Ş:" OΧwa)nֆϘ)߃M݉eXz'>#sfcuCKi#9e`̊3j!`(Г?2c#R("[_##iPj>XUppHl{dvQ$"RMypm4ftbbrT:K_EAZF(F|{K4w@]}YN9DeTTׁXӼ][=QSPWXkdlD !QAֲ @T‚p?µ-߰*DrLdaP_={KYc +10kuvcVZv;r--*b2OROZl] 3܁Yuwq0;8zIpiLG𫽝nIy)\lTx{Rdu\G0ygצe381XcVۊC=HG M3+lDVYe' rTvF:vj>YGs]@9¡XgYOX˛VJ[LH$pE$D8NFA@n&2E?5n./߱-Ccm]j=pzcpy%=9k٬^fap p=kOglch 5j :q-g;g~i'ňʍ{3[asp$)prxK>UbX-.~Th<Ϗʣ}ڿBdel~"&x?YUK gPIk4]B\pV)Kna`g;gۚ;)Vo'f]F!׮Ok/-)rAZAe{IaZGuaL[TfAHs KK-U(y߁{//t٧ީ$l( 3H:\͚CLӗᑕcm>W+\|-Zyax[o9b$2'>Rk A?@,=yar+ݵǖ"<ՄU)n U8/hmf7HWe1&Io+Ov%RS{\ Ukl1up;ߕ^Ѵm s *K[E~?hKI=r$MVR3I˳i9I`F~OjȻy\~ Ph[A'Vܐk3 |ׯJrp喨[[]-ĭ$qBV#+=!캊J |b3ו I,c8 1H>^Tc#+pFOM/#flI4ѓmI;1k)G{}QrB>fm%9ҐI }xy[XTINGa.OE{;Q2;c q&C vx\֬!&I2OkQ%Xb<#|x⹕;~ӡS"Cc$JyY;AY~q^ |hrO pl{ʪ/֫I Q$k9HU-b ?֔ko Y >jj;!VU@hdU6hb_tU_W!3X4S@zk8ͧ%`Ǖ>54Cxn58vy্Z-)S5)yr^-a{dJw?ַqb\FAFs lO?@q=͵ _An?[{wA8_ kԡ%;~9?HÜbQ;{~ˈz*[ۂC ϸ<+kV@#({5.KKzP[ =)j%a,vϥq" gc"0ˆ k^xeюwēMF6RM:([?OJ̗.T*dҠ<I,h$'7Uhaut:(9(AJVG5vS;Hd 1ʑDvyfn@iVv(rJ%d RpL U vIer9F-At>bPc#sʵy:Oި[ fG~!4[lD=kj~&nvMKs9OqК/[ld4hFM &)m|ge\Z+`el{c5WmhU% Z~)Cc0,fc 9g &&k5L= TtU[7qW eɮ=ZIn鋻yI%O>`u{Tڴafс2Y7 DqYiP/\g=H맙iw;I{RcnJ?W9n3^[J@Ǡ'{WM倱)AqXvBvw,1fJp>_UGy .>GF?Zy9dVv/\d BùdU;U6~5 _rG5RxX!zrk>9xqvLoݹJmg]A z*&dasEPn@wRI5㊋Yg<W̷xO=7mFݏF%XWQQ*}VJC4s O,qbs` v?a<;V|(d6\Hq6x=x6B:kg^ L!G:}+G#O>|c\F́q= Iqp؜)O׈1R ׯ|~FK( 07;}D1Ⴑw9yiɆ50=v˱Uʌ׿ɒv nd%;jH\3*rUsڪJ2T0:˵e8+>nVADdxcMnp%dnh;Vz#]Y²1bR2 dp= ɛ *QY./?PPX ,TUŤq6bkXa1ryqNR\5E7 5kp %A8~fShuJr{3DvrXo1%eR$$p{dVJG6z59|;?wn݇uA36sT}GDH׻K;({nOrd%ti&CԅX+|U:<[ x@ʌgW,|$cT?SQښRRoMNxxtĝq7p,hl Td2DeM R:z⧃kQ3!- {q޲ZurN葕^& 8|YdcX}\+-V w>9oTV| l.H?`CTolia+nK( YgE&!r D]V<59NFМ}d#\ .C6"-ez+WY?]'IV?zO8=?r&#e*܎zWm>6:M*B| wwV6Jvv"}܃ĐFʑA 0MPh-KYCrO~oQ77S Yd bj:ōƕP˘*NIAVRCUۢmZ9Z t9,zgvi2YިjH`uw oR1Uj*xXֆsg2y$ y\T "y XF[k/bϹCF=GC4O$eY'tW4Z:{I.[jcf$5ԮyixYI[Z(n`zQDaUq+[xĐ'NGN=wOqޢy' FRmqƘ*0ǒTB$U)$kDIMqtffLS[s~U+Tuz$0}=!85g8메<+#6хmVgeFv>٫M,цi+z68?id6DAZ:\NOpw$,q1\BB0XG=6 [I0T527dH֧[_ɢLªgRRgڸZ7u_lY#܎p'ݮg,+Myԙv6AE,FXc# +SDxqǭh*aIE`r;W/[ #P9Z>v̂@s*w0d3`3v=TrLcOួL\!H,2;Z_;nJ<1MؤC9?48i SX7 wL@ǏkW-`X$lG*I|ŋqj̥dOwZY6^IDy#}cr%K,)O! y6ŷ?RhQF}x֦3}ɔFtH!2C!BF>NKcD1,2GI;fiqSUݻMsJ ,%y!]OةP̷;E2waޮ\\$\)$) 6CNd`mqRyl֏ B:: =kN4[[)lo\ +Ȓ\tL;$c<)9۞v].`c5J]fRxRA2vx sӽ %#VB0lo3ZB&VEKu+[$hYhx y&URlFf l j'Fpa?N= D._^V)Mztd xc`r"Qw;og%k#HƤ] xhli)to~t\氐:~e\JRTM^F-cQ%P ?*{.6Ȟth~Dc$fnt#-Ӟ9O٘HGf?^՝(rǕlkeӭ¶$CA}:U 6gm>V2+,8kx+dhɷtxJm7oyy4l1 w\L6nOڻ.cgۨH,P@k'Vu )UK)+`uO+:U*sruܙF6[G{M +7 FUo${VWZ䷰7# xSBr?\.bO&(6,_jߏSGLJi`pp@>޶-mdmT tR[6:ffczV0.]Fx ,{M4>@AG;#ȸB<\>NHFvn;bK{bH II"GW1djgnά_Vw6yS88%  0R3Hjg$u9{`R!Oz 0fݍX\[֛ p}2G.F['W%I\Hƛx#>R{e@rGOJ62C%Ż Syj1!ko$xԃz5\kA%%uv\r_zkۚ2ɌLmA- qIwus%p^RʡwC'|ޔ|imAt0ɂ;(ӭ0<1Ƕkk氓@h)p=EhČ/! Ia[Zw.& Ua0;S*:us`DϽ+3}a3B|9[~DxŹ@ZF_S7˼-R]KIjryge08ި--<0Bۘ9P0 ұJW:JTQ<ϥu2ٴ6ʀvx[܋*='Or9惃_t]#ghDfXAߞAVGH@d(zsЬRH#Lky{oI&-fxxh'ޙOqϧ~+sh:GXq4I9$?Oz1G0Xn?e(-$_{ְZ\1hQU|pz߈s DUFYNiҍc{8=iϿOsVVyr7)G\'[W23ec=:gsg?늽4ƈN9֕y`w'JR1݇+97GמftGֶ݈m2Ń ]4}%fQ 3ҡL*sݛ@nAhPLdŴ!3qzָ2ܼOL532[DH]Xz97;GfTi-h6,a0=KVqϸ=SZ %d{YFv8ϵPUhA[S)d7Z{t~A# G[]k[ܡq5ʞb) U9I3ױ\֜ hK2B7ꌧn7ܛNyG1Ÿ|R;s/ N2kxDnH6a+?"$i^:^bJN?'aXI"d@u$d~u@4 ]ѝ-ɵ4 }r+ʭ=)rr-Iul  j]F/WFbvH!vQP<1agi(9",JB u"CqU=}+h&OVMih5Gy 994{$yl9C3q3ֲ||DajjS[. 2n/7K: r $!&]6^& m;HPG6XI  LW(QI#udӝ k[ws*"Qr궧p nY7ZCռou9 5]_N͐Yd,IuZ{$u[۠x9=I&iM?EV=?6iyk6j?$#X#^KvuQy;yE0kM!vW9q ~j6 YAf\n~{ބ&\ʺR gFw%֛=A9dQO)s.ױbYdF p?K0b2jڬK!'9ywqUfA!ut*$?7^G8 $ʪ:sSaa0[@o"lY8!r(9'y6N_c*VqeG XoqO{NxnH mJɿO[+_n?\/^Pd UkY9nҰV#$FeJ{G dupܬvvR>V<))V=* {("ޮU$#`VM֕&$be\=GVvw#$f: 60޸~lIPv]B\jL}*觯ҶlK%Y pMaD̓rteid ?'%8O3X>=:S뵖"29?AbI>$O+HN5ͩd'Ae\$3X@a+>Rʁ8kH>E9l%9\DTY`uuu"'r^ԉw0vfQGZ6S*IȰ2Xg5!Ɂ>r.1d!H9|~N U%hgWNWLh7*N9뎴HRwdf1$1Ȧb$p81OC_"SN[UTMxSz{ODC`p}*$ ]G$~HFfwF{1.dy RC8_!zӋ&PG{z@0%vsh,IK QN qvUH0Kdgj,+נKجQ5*VwEx$)6> &A'lXo+^~6ڄ61fG;UOSPOu pN2@錟jKvs$eTr:)L'u/K :zJ}W؎)U|G¸&..8קjijFLj7'z|Gt/ryol*uYL~X¨ޮ~9[VxAZΌBpB%r?`ыL<~e'L` dʦ4Ͳme&(Idr%9|J ayc1ݎ J1kU(}%+i|gNwV{X]!6abx4ߔ Ux8*5}fKr+cN5;n|>ֳl6/$Ā\@%Hr*oXyҌ$pI>+K{}M"@-=8mď 6G Ҵ[CC3 8Zbs̠YuP34xokHlN+i4ՊiufB] scּVk;q^xT@B$|+iBt.UJEFd\;&s~sYV-Y`>sZ݀/١@Mi-YtG12Y9V+D#@Gp R?:eG_ZGx1 Tv'#R&.lu5)].O8jLAoR E9 96_ju<3JT E4%\sVeZJ-ؑgq9›}3~>i%WvE 1 8=C҄mĒR: ȤvsR`PIS &Xp9%w`jE'nꭴ+8H]OZ$K,햍OBkNL'冓g<мH$@ՖFoF,m]"+yHQnl*J8GqҕoOئ& cN߭.f*)=-3Fŋ8830 &~L\yѦG`?a(IO.:nr"B$#ƶ*";z;;{5t4LX70 gջC]\S9,1ZI-/SB9Y,Fsc#fecrKc܁qU : 8A#bFgO*>E"bNsVUx%}{;HnGztԓXofm3z(3֒Adq_,F7?-bCOZ&6{ [=H|w?Rwc4/ݭKipysҳ.\C?$δᣞ|\(ܧ'zdoz; Q ZitH0W^_V3&R 6>^)+yvH%D[lt?Ztk'8);NGon!o7}~9Yϙ p O7b=]?>>qAVЁ]鈹 yEO4`11Nb{U )w2vB\Ll89liNw:vrN8R9۸=J>Q"уp *['RE< 0u9=HwhYox>]@E362N|Jp$B o$u<5-T8ѷHcڥ6g#NxቭluukV,@āF.3m.h1 =fRHԀC Tӕ%T"lq~uW]B;$ S'Tp$Abx\ZE`c67KQjs ːk^zhѼтc9/^ƹ6psZ7f{XeG!~%PD>~G]GZ$F'``{6j;/+Nt=33Gڴt_'ҥKK0B@8ǽGvF3Ӹc͟QYEd {)]ؚ9jTbrX{?\օ!VN+g8nk&]5QWso+[*GRTJܤDĆNA#yry}}7ozhϸY|%q91EY@nTW(G:` JS:a(qzTw0/$`@b@QÑNݡy|KVfd^n0R+:CxVFaOQG-ɑkk2Sǡi{(e!'W/iqRL#m@AnXP|pH,Ă7C۩f/W+Ԟmֵ' +UUD[G,)A֩BN:9UZ…+hg{6v;mIwIq)vPHcVwq4US$mio  *0=XTJOdR]LI,4vq|>}ۖmq-ϭjWQذ[iCʃp  9.bUBsU aJIfahmu'I|dwg;ܞs[cRmNN;JF&}?2F.ӈG<;Ọqހ?L~?4=@M6sPU?yt2:fx^$o|@Kёc2qS\#Ca䟥G R z\)FXa!@ 1Mmx~nUx#5mJ9ۓ;Uig)*A֢zŗwFMʼAxDH0G^PEԫ(;­Xm}#9=z28NU fsx[,߽kP彌m/G5eIaY%iPGs1OZq\[&ѬnXZm'8?JmocgRBeGFzV@EӡUIVa<qBo[1>UmI"Ү0v?2+"E}ދA!!UXnnAcfɣkxdfӌ`ڵS mkx}--skY6 rFYmv$m6qJpBMn8bX)N9eǭ̶0#e{7N&\biP|6凑A.`b+2#3z4IW<SӊOt-1n `Kpjf-ts:/m%*@vƉ-F ~T&H! dv[,鍸yW*5MvEc n?ZRcv`r+&as<VqBNIc9洲4!feQ۵a)=ToZVX`sr6~\,4r6(֖,'#]`w}n&Do7ZJ5`43jT乵qy-ԑ2D4,%G<ZtYϔJH[{ySҳ%=+ҵoi]i"w?J4Ǩ2H6KNoT@8xЗA{+= ;;y}E`j$ ۄy0 .y0W4uV'\!D.X&'ϥRy.&y&ybI4ÝӦ8bLV%mN1zPqG?ZOeS n~&n>F} sVxO•~n{'hIӞ2x5?OV? yC.IO }k~%ӡttkcK0cGEV6p9x1*CpƔBpI ^r5PݣϡW6kp=>ZL+;bں`>a 3NiɤY2\JA|lL`dpZlP*&۝zҚ{YgVBRSm™%zϝ3ƌq#< v% 4r?;ꎈk;c3+<zUY؜Hnp!՚+CETg|HFc`UiM㊛=Yv;S#G!Sq=+V6‚U]\EFUvW^vNSJ{. mH 8OAK=c983[ٌQ[R%f,qu8O;+A$R^^5z}Q7e]L*! )l*+ьJJ8DVPw4wLֻ&!$!r 3N $ ))<>Z96*nT7JN%=oarkH〉X11V3m[@|H ҋr~1#[b?-r qwVۨ/ur& Z.K(Ո#3@\(8jIRW?l&bqt nOjKE*61yh 6yV1G~3.a /9H)%I؊zN6۝f ^DGsIV;kS A-g\^c,$=/_:+BF:1iKJqڼj"X) # .^8v.dMv,YYc%;f$kQ%#( cBwW7YxcK Tv0U<o2h:Fj @v˹>MSmn\ėc7RrR8_k絒lb'$h^I# q-hZusn[|l#=Ee~;Đbn1eN )鴎z&wi"}B C# I!pQ`8:❭Ilg}\u{fcj'+rW=iI$r=AG կ0ij[yhѨ=Z,kF!G$=:KӒR%mò'kPm4m/ʒ'*桺֡$ IB0;ޤeUC.qy"wY s!ֲx3Jz-v&H B'cR A}=+VٖɂMh}ZPtY\"Nzt5v+-nq-۠8d*$d=i-4׹ڍ6KsS9qQ,qؓZ:l eq<*-٫M/Pӭ^[b'0p1YUWp \]}9U-84>(l.I\s<b9Vy]h򢁾}Df)+AzS/u'ɣݚ}OFe_p?TT冬prw &? 6NX,,2H?5LnFsm油RBI'8 q|ш\K4t}N7V6Bt0'H$_Ʃ%Qh2H- g>Y"9S)Z+UiC5ݲqqaD (?^ԽqoX)R[pM*pC#4gF*Gӊl>۵ؓ-1Շ$ܼ_k tȵiq"o`˪Jg]n1XdD\dn;ې:|o:".*epg5mˁ9fpA# 횈gɅў?*j2C;3G ܆,dpHsZOZf>u esS;~5C:qSD J2}d.:.u*xo4GP*!D9 2ܸ9돆!e=}zǸ-ΛaF}9͵rtkLKDD?fxiSn^݁UM#a/=x#k;׷ӊ{ QU5gHFkmc؛pW-=3+Kp5cSIyuFpqTFOzb-Lz~QTЧw {HgeUM@D_azT֖"$l=j:Mn]>6PD)2Nq+bYwc+b02:RÌ"l Oғ/~>y cCӧ4fA}+~ cN~eNW|x~t[!p2G=i$34q T7$sM8ds޼JGpWlm*Ip8?zt!]xz _iߟ̣$M;;dG+,8rx'Ʌ-c20sZKf Ӟj"!0bC)cޮ-Qrڽ,4_m RyM$ (SZQp`~ئ5&',d㚻)Cc RML|VǑ<RI}o!*Ĭ:;ׇrsHg8UVS #ö+[?u5+y1abݬQelcavc*[fw/_Ư!MX;p=:*>1-&c)kAc|W P1&|۫7EnݣfM2qWC9lۤfXSL ]*]J;" }/QX/*+:kè/{S!=1N{tT]N8U ȭ-n{%], 5O 3WF~R̲Ͻ X LRz ʵ&{uM{`W!WI9<Tvw[o\UB"8!>Qk AJ@}12n^b.cTXz}ֻQDPD*O kxB+=rQgY] ч W&xA8u(GDZs|ai2խY81JB2:]8VOjLRJWA*%%vmN-$7ң o,㟥kwY$Ofz۰v8Vi &؊@i`aHǎ+ CV! y"҈n'ߎjG0 +W8o%ץm_Oŀ@C]7M?*"odKM9iYSApضj "X.ei:5׽ݐ#6vO[>8O83qG7OG~\6/g,F22UYt>;w!y~rT '(V%X$sǟS_Y\cV?NNMBd7Nѻ:H65 `ي@3T.sc,w(:XLQI;j\K*am;nVwWdW7R-l|¤tI1WvX>ZFa1 =dUnautZwI=gM!S "<#Z} |mcOb]b4XP1+.n`7122N*I@̎0OB Iܮь}i9.qۑJ(~׵lqzؠW-8+LR'Q2P3*;ς@4Wр;2)%g?NZ=&`ᘅ{I25S∔[N ZM88ui[>]w dqz͎6 ַghi#b\2c {5^;nF(9\" p6GJ8Sڐpzӭ*.K={iMgH!9`(+듊m\x\Nr~-o/i>_$@jfNܗ qϘ8("ZW8[|BxU\N]n}L+Y-S1>4l ~?|c{9g983zzS ۟ʓzzSbqb }(9灞i1>{ӀqIva|* 6K?f8# 4 %NAq{}i;׷yqïJMr @6ӊu-6bYϻˤV۞}Qje-+ݞwl#9ԡ-SKFGnjmg8B݊[}3(V?&+SZ>؟uf8ݺG?`OxIpz)XTΪRs(EW9w XvZP%pǯT""X{; =~u=[1A@8)p\@[\j=Uvx. fA: 8{8leV_ϰc1q+}q܌w) 6$!fc"H f(R_2fAbYCI bݎءZF0+Kp{NùN` wcq$JմYE\O\cQOtk'1.JĖ5OMkKw.2Œ8EzYJ7M(.MY{U_*?C54wB[S |y~`ΩO`F}U-ɒKWrC,IO˫jz>5(^=+!屸t6S`8#8lUg?3 cv{JE+B y pC{:4I"0Fџl`kW"q+:n2ڢά4}7.lBta^#Ҳ\[ۮe~vW3lұko{p*8qLo#x팓B8"4֦:5rN,&Ea0~j"\7j%Micηt +k{7M%Òw(n;Uu]*[`KUّPp1INڣwOw3NIR  hKlcƦOܭ{8'g;0<'銳ZF\]\[Yp8OZs 0UuSStG^)UpG\s;ka3sZut(7Bgxw)vv{fP[U5ϗ 9i$qIǗo˚pwOқϓ߯Z`8< `<䏥v7/o'8;zgfL0'p'xž9J0I9=?@9.>ː1@3 9?0Y[ze 'c")sOfD;Fc:syA?8ʝp1g.fih1g*B_pF9$`ըpJ ސ *:+Ҿ !O^ =HeA%z O_b"Os" OaePѫ6EUj化V vdo_3;FPAjFpHGr9i`sc0=7cҦ謢@f`7⡊ٜ,@UUH&Qd=:R9 znmg2-o $S*Ie2A*8C*>O#e[YnR14*@|43Ybeܠڟyi >YM>*wYd:h0~nCpP'-] %m 2]tSl U&G+.ҵBXLoIb{"{"1L_$wRY[Z=F89jՓF.7eCMc D& t#?R:1F׻+ G0Uzb f)w>LD^eP'5qq* ٫_}K<̬<ҫy)sԶwa/!K`Ppl,NzS[Hj!9Pz>)Vv?%Μ-0X0s)VMrDJi_MI{}*ͬ0F)wN*q O\Ӿ) I\IRM2(tM5,2Q&>>KqYBZ4d5x躭ƟyZkH;__K9"x$e-#8A}=NvKFq Jqꃒs_MZj148;@t]ߐեX-dq+?J-Mo0c㓌VZI42I%2a>KKE{6m |CyAee+G&KbKh֐=Kn\Or$Iodt9=oP&V#HUx n*:-HM]=lP>ffw2T-\k3$Q4x X5auw]7O!ؠ"RTޥ֢&[qxï5VNI⻖TR`wE|V,c,͵=H&s9@$:`V2m)7Z|k: w*)b9)8P,P}e=1kBitGF'ؠelq=9T$RynG^Mzr-yPJ|ao5 RX$qHjaZSm%;Uy5˭M['A|qǠVeHGeq>#ï5VOI⻖T3qޫZ\D]^Dr<8өO %nU1{޷l{_qZvQKmy *]qaʞ\׾*+\G;Eyj̒$DVg @3~^ *(VݒI.60E܀ <1{JϗB::uFYVp1@rs7 ^8ʴcˡȫ}$^ii c]h&6qd:+dWyfCO>4vsMÜ ry9i/(<:zըBSӬucŹ<{@M85T ʘ1nzJz&e)uB(8'-jjVJ9 DE`8#>83Dt_sѰbBiH1ңRH9/ s6c$$wJ'Vm"YJW`ܹ`_CZ$F_j+= mw:^S$l>bz ujAQLf46nGNE68tץfkW>K OJbk8n\=č$sϥiAþ|3wgx? cMyyӷ֔g$g07#r{(;};uzPy҅@ŰRnu400@B= S|U-Iw"C Xp8ϧҭՅ;> : ȣZo`&MB5X=*K ު,E +lJ=M=EC(*A Fj-|1jGhqcQq$V*jrotݦȝHӀ?*ũ]E- 4J%P ؏IƾlM>C;Qg] |<_*#" #ӥ^chjiތOXۖ;J5(H6B?i'̌cY=E;mk<`f"|azjPŬ脰h㴪5Hiz$%9+[ $;/?*35F[as=L1<(ZqnKtnٖ҃ Ѯ )b6ߟZ\NJ;VW*0# kp9Y*+f![F^pF(zwǹEb >5Ϙ6܍CΓG-r +S`vQV t'H>EHP~ǠXj7h Px[]:xh|åN霺)%<'My E$jPr<{~5;Xv+y(6nP++YKP5R~30)J[1ٲ^ZV0cgO֮Xx^K}>-Q5^`H8u^=;oۼ3%}3үZL2Hѕ@-ozs%NG;PϫC!Ay#5LAfx {Y2eULIYkWvq\"FGϷ^*wu4XE*[g̼cfK.rHX④$P?zK5[qpsr^|[ĠC] Bk_-Lj'ZQqnKj\E,A\YSBv6ѿmMwNUIVa<qO˩&JQ]k1 E듀1B8sS-pPxzck4mUaF㏡fˢhbxfIc;WEO}*] !R,B'ct`!۞Vz-Z֭p=a^$t*Tf"0B8?0Ddnu-ZF(zwǹMEb^~%UVXsYh)Rb?NjR;=2BqEp3m |0`߰E(̠Zrbdd q^GOS5ܳ.I ۴tYpӽP48d_D#%8!} RK)N#/wq`"G4Q@l ~R{׵jYP#yU!^>oZ(VŢʾIoImmsEJ)Ky#nYe< +$+HjCs*0xrKmNjkg?8QQ5Qz\Opr}5(+6|[kKxDr73{6OGts玞QS4E9K  g=W,|J(lsͻV20AS̞x`۵VfK>=a$ G^%= 9Ps2?FM6=*Ҋ(CdַRA3WȢ9mVr3xkӥZpE+V~Ą֊+h2u9]3qxQ[AhoqɏUGsZ>ź67jVEw<7LlXjXD`ۭWOtRFܠQdU2Jw{~u.w[@DH&)-Kwj3̱oB1aˋT{s#ebgV1ћUz#E0G-M[+cr\-LdyrMG=GQEk'%R޵MO7}[ں O!X߿(ڊчzul0wL{s(/qZTGUN~vp76Srw+Hms{H YpK3dǟYx}E 799VkCR|BvǪK5H2zEcfmUx&WO8G-M\+cp\c#˒m98;b$DP\GY2 y$@ĎNrsEK5lh_#t층WX1zEcW1Y\06-բ,[p dyrMG=GQEk'(g%2 0:I$ULFQ?xu=袳={~Gfotoxx-15.11.1/data/images/shift_colors.jpg0000644000175000017500000002506412616075370017243 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|tonemap| 4http://ns.adobe.com/xap/1.0/ 158 302 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? KhMK4k,b\gJYͅe9=D ?%W-{cRqްS7:hQE>++^Mve7¸ 6 +8@#9͑`>RF]ڦ_hxū=ǔ\31?⍖lނ5 K9Qs)#OmWIJ+mn9K;\{('ןqN5C~+2'{ԆDC4=Kր`_QEBdK,[%zgJ|k j0]K۟ 9Gq}3vw|iJ$ttp3i9Ś1{Yh7 bq\Iy r2xpHHQin44>QIU>\OB5A~)m`lz"|o4hDfNwY\F]xNKsr. QJ:tWS8ML-C hH# c+Fk]lT pEܹpZ׵.gimgq(R.؎G= `wfX'٭\7zoCjYge VFY#8/Y o.b Y$\d<ughgh3ϴ?ϴ™ލϴϴ?™ލϴ?ϴ?™ލϴϴ™ލϴϴ™ލϴϴ™ލiXX NyͅqI "fU]FS@&,:HEkZ8>Q{{ ui67&f1FBi8+1n.2OLewwp ӕ╷+72 h(WPA^:g2UHU j*Jnw*fI Μ#6x#Z(?u*0_ʪ\ 4t~8zi7WdYF9֯ r-h! GNc6&m2PZ[GFQ`RԮo<tK 5Xb },ʍrJ;J⣍m "Ì6iGGEj"h6Ȩ?QC wٺ[L;~t~Tm ۡǺAm,=$^Db z.*??*6ʋ8zZ$J y'}ah[_宺=|/" 7-k0\QGXe#Et{ږBtI+>Ӝ-5mjIT֧9˹Pl*[?"RY>bf[Tedg)>gd/4fK?2\ZUْ̿//rhQ@ْ̿//rhQ@ْy m(!rǩ@ GÚ";[8P$vMR 2E!򐫅]#О:|)~oz =R k{bP |sS.7 ޳!$^/_3~q:eHdP 2<ou\_(}qYȸ2Cy,6;S!CvϷC5Ӈ~uqB;i$ff-=k]m5. {t?-k`eUWlpK1;lj,ȼ'7&cRĬ'$*=0 : gZ$[{kHo>F 1<k^GnZe $@8#? PT\ ]YH@8'Bk]o%4"]9?vNV=2$XE2~Fl5j漷W#n%FQ^H+JYvzI.y1t'3޺R9.ǍJe (Gb|(@` }#S 푓D DI6g㞕\jv5C"<=[4qGdfA}*͖kBZDcFmf& }ͅHDxAI7$W9I|MBs4Ko4f=?~'! /^o&I|MBs5a9I|MBs4Ko7]~h_ȿ @MCQZv6ߒ3%٠]y Ismdh$^)J'RdQ U=p=kH+34ұ_Z~ >թϼ_ )V>ߩj_+BV>ߩj_+BZ~ #Kr(WpeYS[WΚw\,FvһTU[alR*s`YZZ;fcy'ĚMȢÌmCiw X9),=2kJA'/(uqYȴ:ϛ;W5o⻙/3&ɼL= m8Ka 2=fɍ-1&wǸ*/b]XiR k,3\l ϡŶ ơiT 1d҅-5%{{z헖B7 r:T &-+v(<J7)as@)RxhC)H$AP>UjZ׵ 6-$1pJ(fNpxI~$$12pN=CWW U"f 9ی)G4V ["6)!3)N> '%#h ͷ#'g4T/4 8=Erk4+ 22 {;UOm/MKGNl$񓎾]s[ibbGFBN]LVk噐ds}Hr_36?h@:?m?tizQ@m~n?o&` (m|QjAzQ@m~n?o&` (o|Q !;^hm$Htx'Vo$j. =ƝM4L_΍%#,v:vwaږݹY?OK_,MX=?ʘږݹY??-s5c?ʏ*k۟R? 'V>o<#=?-}.GO&E*MʷCR}x'O(@t2nu JVNңSk]n#-( 99?;{HD=.v,>4˽Am$G:KpqHז$DGb@,C|=ꩨZjT6NH#ފuƟOjv>Eݡ{Ҋ%_/) I$Vr_*iX)Jüj 3T"!+9Ztڇܤm$jc}enU5+DX!(D^Zc?֖:w[3cSPi٢) *jIIWotuJ]/] e];cphMgPFV;P+\-6--Ev0G=ACڅo{(39raJ6Qov:}ҍҹWv_eG+/?>m>»~*?]_?gK}(}+we?gT»~*9/ΗU#We.YԐ-q ,E  t; \b\(Gr y<!Rl%)zaRyq4EQ#yq4E\_?@yq4E\_?@./ȣˋy(ъgOQ<QgOQ<)\_?GOPyq4E\_?@<yq4Eצůj7~-北zoZK@./*+NhZ'/` E"k_/-EkP%B$(((((((((Q֡{5˼7I zN~ cm0 ~jviYkvJ_2Dr8kG:D.nԼӘJ_(3+ǵA 7wfw~ѷqvqzlHR0z3\`7+5֒^\\2sxlaH) 1sc(J5V2ih}:*ޡgB$<4UHfrVn$V~'/y,YeR$qi]yk2M}F?KOj1^E$[4f'P@<)ڌ2[=+mzU B-2[ۀ([`-_&ss^_߷fߓok"8&@uiVemᔒKk Hn(sTz6$Ct,QرiP%BqMxjΪ'w (q?7q[hѾۏEm1\O?opbW}?Ə4\,vأhoFn? (q?7q[hѾۏEm1\O?opbW}?Ə4\,vأhoFn? (q?7q[hѾۏEn85MfLOk;**.A9hoѾۏEW׿?o}?Ƌ@ϵ4 } [S\CoՈgK8[ 4ry$<^ ?ʹO4hockh{~ r7q[hѾۏEckhkkѾۏG.+-ܲ\Yˋic"J6 ?+7ZvR H"RGfotoxx-15.11.1/data/images/mashup5.jpg0000644000175000017500000005504512616075370016131 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|sharpen| 4http://ns.adobe.com/xap/1.0/ 371 454 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;D" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?(98UVG۸pou)\W.b2l y %yVxj,>ڦISzGҁ1nR"8 3%l^R{.-h2L~x;֔#B## O8;#/@~gQb~Q&Ý q U;˛t#Y@v`c{5ffAXuR~B4K(%rJop-ZZ.ǹ7DHդm՞{iT%bpNյ PMڜD[ 2[99K*ʕ#Ƿ+t4ަߒ3CU$qk Jgk#H#)=ͽo~BIдfӚz"is9$Q.N=Ej_ޛ_ə́ 1u#7oQ akK7 $̒\pHT3׊zR}OQ ލ\eOQ ލ\ _7?!F*z7p,o~BOU}oXE 7?!Uѿދco~B(WFz.OQ ލ\ _7?!F*z7p,o~BOU}oXE 7?!Uѿދco~B*+#+){?G+担`Vd aһu$[[d;h9MĚRX)t %pN_0}굇{h̷]}p  1>gϗcVl|+hΥ! \[qوgm-lՆ0O֘:o.t{({r&ycg_mܩ'5cjԣF )umvn *zcgem !) m8Z[K[L7vNvMu%? դ0[=c*PYŽx9^"([%ȗ 6rzptiGs-VK 8ȡ,,cdi# y@5)?Tա\fF>`rmvf;08)/4bai}:;uF2Ͻ;>a:;fMWLyyΟw<9lK+ghTcN2? @qOrVvQOpPL?rJqҪjtK.5]9n>X+#9&BWy>%}"Uٸ嶐>\ZEVr҆5Xz C-6Zuĭ,L#iH5佁x2T!_~:t;{x2o H#.GEϠ*laEdAnw^<9;đwQu/.IXUKXnejKcNhT[u4I&ML2 B* dMhIeg5WRq%gOC_kb_bM`iPĥH۰ϧ>H!H&:(A=jlh\𧭻6vpɎ `nCF oW #AicL66ͷharY'x`4sZx)gU 䁞Hg'y`M{m[Ku>,WQNOvHH22H *eĞ-SHq#닑 *qdTmFZԨ.(!ΩO´@ Yt.,쭭61Dxq =J@vbȸtTIC{Kf (񴑑~=*}3OJ--" 㓎5vS&kcg&P#@˂wi:n@p0X`\'ZSóɥmeOK=m-Ym1hA.VfxBΪA9n:U䒄yW%Ux߲?S_.d}}y5@'QY?@'QY?@'QY?@'QY?@'QY?@'QY?@'QY?@'QY?@'QY?@'QY?@'QY?@'QY?@'QY?@'QY?@'QY?@'U-F-d=1kVUiQQHDMbG?4KEE7~sϼd}Hs?Bdc:MI@s?G?5IJhJ-S~Nw??4ys?P}?9;PD_Ǐ"Hr%LzCsvy@s?G?5ڏӝOExD_Ǐo;j>Nw8U) <>Nw}?"/h"<Ơ9;Qsv ȋyGڀ'"<Ə"/jӝo;jȋ<>Nw}?"/h"<Ơ9;Qsv ȋyGڀ'"<Ə"/jӝoKo ȋlQN̋dQX?"/h"<Ƥ#"<Ə"/jJ(?"/h"<ƙ=P:GQQ?€'"<Ə"/jӝo;jȋ<>Nw}sv ȋ<>Nw}?"/h"<Ơxy;o;jȋr'A^;Ooo*[V}>`3ʍ IsZ:rdAƬoZR}&Xnm nc)F[LVF*$ d9ޤ:ޜ/fpB䏛{U]w@RATKdbѰ=PprjxLAl6ŢwNamo,2="柯}K-%U,o$b)zP9MĒ4J[q]8tw]]݉,n&2XFg,dҼ55Ip $a{xOӊ_֞>cƲD#V`Q=^ֹ|5uR+ E%O"I Cʕk)2H29sG>Qo>sNG>Qo>sNG>Qo>sNG>Qo>sOsG>Q8ƥnrTˁ d }0#]s鶚]k2=ºB@9[qROcm.m+D!'nN3?ZxNTR{t%Х҅06"ºjjqC}t<{@Hn)tSBOQEm$*IBN~e7tg`AFA떿|׷iye;$mӑ]<1,@j|`2D?UW^/Aɧ(Vh.ڹ8ϷJjF0޲ X=EhbbHa|kis+I.^+*g r~5 ͢DY6\"H=pqWCE zir^Cn#!N8t/O_Yw~15EHV''qBtcRO 5^)u{6*(  [t'#ppppGTzҼztHŝ 3rjo glq E\RM 1[xNT{{m#g rp'jQX&𭦿c2Wx*$n{3EpKE p#6cU#M>OqH" w[LWkW1R>ޔ߳]q5jC*_?Z ؤ$ι QާFX3G+0ϯ/O4(?G?>gO4AO??GAGO4(?G?>gO4AO??GAGO4(?G?>gO4AO??GAGO4($23s``{O((a2ynFG4AO??GAHŒ R?Ƥ??G O?/O4(tKefgsvih((()#ր2"V>tgYQhW 5~{xcőFwwAHkyv2cXgl<[@9%a;n(խo;B^F$&eWHК<'Ώ6nW?5FF<}E$^z^xA [{Bꤐi誵:mwSxD7'*O^¬ C1%8#sZb՝P\YUCҹDlP^ʍ #,?\7Nwº)hR(* @,Gd.̾P3&v3LQ1ރ,GW$evMun`A{wC?쯵{졷.xy7)a sIwV1kiբiV#0>{P}:|mA <ڢoj: kD+*qSdӰ&wT3\DmO+['įxq.g Klhqٺޤd,>jPUZ$YeWp~o(4hY'g3 >luǭsA⹵ t=N; 6 69ѧ\nr2b/ٲZ*latfAk7@(.A 1U(jq?nJT[F~?n,Ue6_ _0EUM>V\VvďN͗?n,QPgWT'?--$dPԌ RR@ycWnFߘ6 4]ێˏV3]x/+-x 3lq׿ij3-2D!$Dz]08Gך߆}~RVfD+ >FA]1 3 ݱ?W6F$[}·]cV ɦ e/q4pJ:I1 REVjGSfU'?Фk*A*2?(69B'wq,#$ Zb۹h'$^iS[I O2̧c`Cy?OݺIqՊO*L\8QԆQEQEQEQEQEV?ouKK2M}B]yI*F@r).c2H2J=j_1ti#-zݶ%kŖ`~fsKY}i[yr9!68ʞ83_Kpn%M5,ʅzPs]9g_ =rhBg?{4BXi2$z $)ֿ>%Hx'1V\x~H#Nhb;CAV? ɘ|LH-)gKcJ+ 6ߧ.`s{oA@of@9KD@?-?m "' #\F my5G&yg>a򝾙#Ԭs{n>f=$$YݙIL-6ՅnvBŐb@$~^LOShsQ[,2.OQhZ+'^Ɔ.Фuy˰DCcQR7ePҀws].Z:Liȉ{ 358'jVQƭejZPoh)l|Q4sN84@ty"sգnPA|!'ܡK <7nU|l7pZECMב.o(?"?6uq}u~Deh0P=kG˟(<[~?(DTSGRs@qHx?.)S?EKwb.}>81k?QyRyr(?mׇ\?OMDH".dO$RQEQEQEQEQEQEQEQEQEQEQEQEQQ\k+[y1Ov=|Cqcpw#KK%O\J*|=T{bEgD@FXz:^^x[{QDQX ?)ѧW2,zeȃacf6GB?:v\+#%U FARW}Ihsn-9HZ5b"D0m9#.űhL 2%@/OGе6.X'Ԯ.o`$lalڽtu(c_ t ?Y:^ d.}+V_W~?4:MK*)iDv_xOȅ#U,\uosI4y.(qo(; 8pk#K4EHUHEs^%6Es#ZFl|l]UV1y5s UQ**{i.淊[Gaf2 sZeG cq8LP>@/OǣF$'K$?J2!#p*quٵ.֟ rj?i)?d ︿>'O}f[QmG?L(6tm|',ӑo9KmG?O?/.sa?'?~/.6?֟ rmur,}O풞#cP}QOo9G!ѽ˟y !Tm t~@QZeNuvb9֛k3Oo@F7Pr 0~|Om.O#H̹4oτ_]m|'j?i(6?֟ r'l?MVB|/j?i) ETۭ?տXl?Gd ︿ڏZ+>ͨ 'O}ccE6(~/.6?֟ r Yh7qVcτ_]#]O"KFBGY$LPQOo9HjQ):l yrʀ'P@bZu@J?kvfy)4w TYQOo9GٵVbmG?f[P*@'֜ӫ@?ng=[P*ٵV}QOo9@(˟HVq=)~ͨ U#R^Eդ,w~Ti!M8< KM ,eFȀI ש5y[homm9܉ly + =룿nnn-|w =H#V}tl-Y34q xH'+v+D=w0Tot{&m;zVR' %5қ#q"+!>O*h!Kyj-0S4CpshWc=i]^ X9#98PjFދ_"rMPv~&vm[J!`z+sߦ:xKH9Q-XD pÁjlZ4mM5&F(d9lY1#<jf8gkX=p2}錥KΣ)r)T?╉!@#j*ӏ94Uq8J5jWFы]'*ӏv{9ϓX=:ٞ ҒUVϨ(J'?Ѕ6|h,P|틟lʖɑ%o OPT /;N3K.^\]ږ5D*B 0OjqYuW`.ܒd B Qv(Fi%ӌ~?PhKƬPT_cHxԸ8oq~%׵Hum.tJL$jv9zWKycsYH.$s3׽:N{WkIlyjQ`4j1=|8nM~Ԓ- $YTH*{\3\L), *OX'UIcez I|wXyL]Fye)ޜ7]*+DiN+1vstPXah☆(}*\S%oCf38errX6r=]Ӭ㳳a!E?&(;b|Mv"Ҳǐz~ (&Hcm;b"|&Ѝq5T^j*aY#@߂7|XojK[٢qf\ٹvzg,6M5:塔pHAf1 "v RN* w(0((Xуa R@?mn2o.6_7V& qP_ٶ޺ٿ?m}nOcSԿ >Կ(H k>'J蒮#I0j//w QrY=~$P}|a;oS++d`yK@}|a;oR)g`9$Qs rOϝ_6m~W(R+K6m~W( ߕ 6m~W( ߕ 6m~W( ߕ 6m~W( ߕ 6m~W)aЬj('95[Ť-Z(ϝ_6m~W*z(ϝ_6m~W*z(ϝ_6m~W*z(ϝ_6m~W*z(ϝ_6m~W*z($% GB"Qҥ$sEtKGWXУg psǥZxZ3sQy%<@A8Z_'՛L$> `y/^%4ȯ\N B5o|WXKyn;qp#Ugt'h*23pjwZA-@o-@^7L=6L,͗U&i1IF9;Nkv7 l>CC}0 tl/74ڤ:Eռ)95 "@H߸׽[vk1YHn$m}83Bri6CMjr!HҍKW]@5ղ&(q=K̗o݄[}M]K/غxfk8# (Uwm ^H"3$d֧GL[Hb[o;Nx82+Y^\6wcKM F 5e::v&48emz1ozntmb:si+iSGk?wCU}חe[m H#a&$Qq<3LsG=t\ƢqmV5wFGץK1N rFs] m崺K$^7\t 0cHǥu.#yI֎Ԓ(0(((((((((((((((((mp{E';%x_xTPAE7è}jOz7tPO#P_o9J '澄-H?\*E2(jY,%M>(&f\e o#ROQ@" GF_rAR'>EEbNu{P"z|Ⱥ'htTe?<Ə"z|/}Qn}E$oFӒ#._/}SSϟq"P_'v:=)E?@_/}Q]X4y_ꌿGGuONH#Њ}Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@!KHzP+R=QW5P/dQneEz }t3:Ƒfh xÕ'8*'c bxr,9Xp8@9LF4>)45+iH% 218 405 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;c" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? ;XMK4k,ĸJt2"қl%BoK챇t~RqްS7:hQE>++^Kve7¸ 7P7 +80>P=wYDi@=h/C5gˀzf5QԶձkŖsu1s2}ʉd  :b9K;\{('Sk>ߵ ̵彎漉b?EohM{ƿ@ao5CMg/}j]CXN֨[jκ^Uơ<%I*0$ Pz?ٷK+i#vȨJB;Sm/ -0?&S¾ujȗ_W 6.C,xQֶ<j.֧PlO|ϔs}t+%hm(1E`[ {xfUUeaEq^4jR_t7;`iT9돔}g-hdJwÞ:qNzO٭G٭^xW{HᾎdӣWyF9. z Wz֜nAeїʞYO@__4ϴ?ϴ?™ލϴ?ϴ?™ލϴ?ϴ?™ލϴ?ϴ?™ލϴ?ϴ?™ލϴ?ϴ™ލϴϴ?™ލϴ?ϴ?™ލϴ?ϴ™ލϴϴ™ލϴϴ™ލϴϴ™ލϴ†u*mbޗsZXmD?.y#8hӝbs(+J#?UԴMQqD+Z/ؠʩ.P⤬3qw8w_jLJw+72 ha_/D`Τ's6?-Nd;t$FmG֯_/Ub.f$$!3yִ0_ʌ/s:Fd*$P4KTٲd#+O ڿ_ʋ[bACNdϩ8RrB Q´vjq*,2EmwT0-gZ{WQ -%0т{q *sLڿ_ʍ\f'=kCjq*6s?yִ6jq*,3G=kCjq*0_ʋ8zZq*0_ʋ8zZq*0_ʋ8zZq*0_ʋ8zZq*0_ʋ8zZq*0_ʋ8zZq*6s?yִ6jq*,3G=kCjq*6s?yִ6jq*,3G=kCjq*6s?ajq*6sXVc)g<KIf-sжC@RN9犩jޘ/K䙖KTdCi9Ozwbfu+85Qw{([#0Ve d iSXӤS{F.Պ1$-.8NsGc@F)HP U'\ \խ5fe+xb-JIم8\RC|/leBʨ3ۯzEqϭjer˨ӖZhQD.Y?(QuWcZa Y .ȹ^]>\i:{$Dww0;F9G*f%綸dwk+ܭ ՊbCISЂ[QLAF+#[.e #0id9z?UE߈_BE4@YK*;I#W1sxUqn'kia)v'[]-+lb;w3# y@n)&펯Ӝ;kjpDitlhZVf ۞j^kmyCsO I 1H'fsꚕijՑK7Jb``TdrxOxsOq$@'搪__ۥ%"NK'P-|Λwtցg^QuA{XcXѯ0+i fQ\nZ%پԴ˓tR I*3+ l=1\uA{H7J׺Qlg?QϺ>_/"@/;/H隣u$.0=NuA{H7L\V85KO(}_E#YƥV!&6\N  Ղ5wǒ`7cךbc$*쌅xh\_ǘ=2E6r{-(!IIUQP@`ԩ 1$<22ϩ(?!RV%彵^D,Pc\c55YZ566Ȓ YBB$ZEUI-4Hdi "ҴtTNT27;TPkd1"0}t.ɷXP_(u%R/qsx-cH3HWfzG4hb䰹…`T'TFps098As YH35[-ܨr#~4 nEP"#k,+K q2jsmo{Auo63OjJ(.úm%mŵ-Lgx81>AuEq Z+MuJ28@d2=[}jj(+T$AvvY,\Wt: V=HQ@ ;jn?Uj;bU?P1N/--a79V3&:fOYKiylRyCtLami7,ЂqTŶ"KKckG>\{oU=3Hn Ť-'r1iy-$Ua#;Ojcw^ I^Z1ن_~µk'O4A&"FНŒ0<Zƛ~Pُ_?ښ\j? 3o5?Z0.ر$UN6[Q/-Ŭ :*[_ mb@3b1@>CFn|{KԊK r/٥8`pFB+|ЍI@4]R+)(W 7[BK$0HO Ӭljf>0J:˩i\g5:+(_ ֮-"-.bR6"(瓓WiĶO{1tcC ]v`rlfmX:f]N!b}R{f}D֒#'[+H. ˘e2C dֵLC1M~G.)ݟt}mK{y'o˾@ X=VjE4_J2 K0hQ,vp튟d#1?q'F "56%]vsڻH&eI2c *u9ų6Wp\6'ڀ8;ÉgX-_ VosDuֿr_*Z<!R>d E.3ڍҀvUص+$8`H?ʀ'j^#;C  Ҭb cC_5$x#G((4b]ҍҀ{8r"FPM9gW+$; R>Kvvs,IF*Q\7u=m鬴z}**MB<֕'V\O?_?¼;irkQJ==OjyLְHՎ=iS-Zyw/Hb 5-q? yw/RQ@w7H"T(c'R@w/AWR^Lx# %9 t*j**Ek*h?/iqܯZ@-p|J'=3]My[%BɫD3׭7aMR 0YbnI '%캽}{K[cm8^Hzs7Ziu[KFrvxqGCw:bY[GzReu_,U+}h}h+&“0Y2"]&'GKdA~:yn>X~/ҡ{Tve{aݟ·(L2p,P{9f$KwltZmzmME1U6t;JnD+c(((((Xii7V[eV8ɪ}V a[\?v0@e_w?Jm㺷x%߱r27A +dG *Zt{K$ۥL6r)KXX\[X,:T&.D cp`qqG%mJFom]VycP8ߵX \?K,bj6>@ qqp8K{|Puv`[A"h߿fݸZ]֒K r:jOS %hU +OjsǙXҜs5y/5VuLpBX1Bi M#U?P*]֒>F.oh (0 ( (  5 OEk*hTń$؅NSTӵ-RS&w.`S.]7Nns <~Y$g8sڬ]۹+x!N7)ڢnMi*Y9#:t/#ص_-1h'@$$BXWW]rUmǛiZI_f]Bj_6/Фey4}7ȿB2/Р}7X? 319 282 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? KhZ%5IT1.3,`̷''|Z\ Ky,w wQ(>w*Κ}ntOEJǠhןqSlݴYD#_ N7F` Fs]ȖAbyWr 4jD-Y<}iyG-Km[(k1As)#5Un+H\pJp2{b9K+\QOMѯ?{_VmO%Czq,hQ7qހ`_on>g, DZrTu=:W)8lO&?aH/Ju3_j:m֟,a]YP. ]RXk>ߵ >k>ߵ ᮼAyqgM7h s*e޹-҉[!@0p'+0;ϳZϬ?“ɳ<|lZ)]m'O>]ųWT.0ăUf ϰut=I_on>f TZuG_:xUbB?/lYYjJi#1v_,ujWӾk>ߵ >k>ߵ |da$7z1!ghgh3ϴϴ?™ލϴ?ϴ?™ލϴ?ϴ™ލϴϴ™ލϴϴ™ލOX6UHрA8F.?? + [3,UmBR@&%tu+VKTGGCfwNl&4Nw)Z9̍뎵GGD`Τ;MI Ξ#6h۹QU 4tZWQ`*[Ae#xc!3E`(2Z8_FTX.f2[4iA "}1;SfFdF1;~WQ`G5;Q@4I YN1>ִWh\͕m %񃏦ibJp=:VTm_ʋjG_ڿ_ʍr=hG_ڿ_ʍr=hG_ڿ_ʋh Q\Z3b"]nUmU!GPШ 1PGC,1Vi2">FF9aF+K{HUKUf}1s|߆Aw_-'gyt,o2y8ZRdOtʭ#z94ѤiB6(Nv _.ð"Apsrdm`ZΓAam>=tc]SZpAX0Uqڐi! }o(gހ9Y}.m+=]KnWȒEr@`GjkXIV:9cwa?fbtml&+T r=qm6[vLzc)]VĚhifH ܳJ$mOMi73Px=3WEe(8+@'c6/84#Ʒv<Лz䊂o[j7QG'CY{v}*KT}2ѠTa*EӬ61y~jY%avVHK椹WT?zv٠?0:{GEo%zeC/ Qh:>c3l;_%q}@{UYۤKS x"mukY Y# .QJ/Pd|Apj}+%^0.ۃl>m%0qVƷjrfF܏vf|{jj:,wF]ţ4 3O޻tt1[!ClJ>OQǢDG٤nV$c#"WIaKks$س1_ WC \BA""ė ,A l9A I Q$qUQE ((( (qЅܨm?S$ XvEF S?Rڦ/ ~Uh@jPFonȃ,#go28Bf6*ڏ͜c?ʰCgwy៱ٶۙ"؟6|{RCrDNWKbY1 mcvlVHTp?Z`ިVzkiNA[QU4٣O-6y 7=M.TW46Kx1J8èUSɸTAO:1v\g#VmfV{Yc Bc?]֍ք&pڔ͍޷ lۺ@>c/kY@iB OWIqyHt{RKya!6ڍHs=gVq7QS#^O˦@b0YA w{溝fY>A^*|%E*qpr#_3NVt8ޗm^g۫n>qX4iWQٲ2' a^zѸQp(vnHJFÚHLn+ʲ4'qYei~ր(kk+xNKJl\0$Zm" _ZBo9QFm$C>xI! - &T$\E1Xњ)QHaEPEPEPEPo@QF {(Hjqc=SUOݦ%Srjɜd}j̈́ wG;YZ^t=}7r=-$Ԇ`F42܂:(m8~(yǨ氥u N(m| Kp6H>٧ɢɩ4ΘwGXUc$G#>kX^W{iR*QԵѡI]q5zŠ(((ۚ( `;({aYY L4gJu_m0.>v? ]ŦCogsyã+aKg pF:t⬦|e|ȧH;WvUC6nꦓk%gi62i`E(J NǷ)#i:Sƥ R#+2KmVf2- NzJoxsOb\Z:y*GSEQynnҨ9^ZLH7]kׅ9"97 N9`*hݤ&PYiڢE!kV_?HQ0Fs;T%ٞ1kY #;Tmj8d1LڡUA_t X/eXH)y ) ŵ15&Gyc` PsVW@A g5IjG'ڼՊ9T6Ze H *E (qЅ2-? %B ^}J{Ƿ>mьIOZP%c H8 ~Y|1F>ec㩪DBuP6Υimtoĺ)-PwRs RèjJʣ)hFa D-0Y(|Brq lRԄI!wn6(K:]6R/4*SHj+ku Hsi"-ỎgțP95ϯjڣ $Vh 屟Յoj:t:;ʪO:g{U{q;6,φs3He(uuԷ.K4^_v 'Bk|z+ɽe3|ߑ\k@VtqxCk<26͜wZ5Nm*N-A6Lc!mȮ!) XpZz 팯zѴIyC[oiVt?,Gq bLTht6R['yr۳v"^Z꒤-|5V6JF0ŐN} hiV7J$%$PI^4ZV6q 1 {-$ Hz)mncQm[RO%[i(I'͈`6G㊗p E%ą"%`d=G!zDng#zPcAsu 0@>57\NJMqkBnގɅ<;kCZO6"M3GHzg!P&cEȞG-K,En3ۭ8x\ MBr>n9=ZvwMyl&1dWr֘kX_CKlms f :*>ѢB@j-ڱ R2szlι heeFev팹'':iCyz-VSx#m&c#p) fNQwm3bU۵[N{vf$;lɎO+m&`gM?@vY&I61ff8J]Pq`,!RJ(8Xjo{qsnS[=c#H>R{:By3-@\_TiPNǭoZZcx&M4^ߑRrF~)۶Q[AQ+y]랝hO4Pj~y{ " H) a<6טB-{p*d`<l펽{ PG.>gn^CH&?5 {L2@tUdM;3& v%Û.K?? TA\dAk$cTBč;ǥ^Oۉ䑡FD ci= = v:խ/,Zm[L&o6FRH`aOiP(- ~q:NI3;gar~Q6wWڌEHYH˷r'_^MoaPyj4mp0`9b@*+?$M> knwEB;o`U"Y@@vV,RY], y8 u)n#L9KKf6{c5WM䱵xyB1eBW28J5lӶp]#'O.6[Ct6;WDI?Œ= k{8 v%dg~@=T,rʚe˟24iB sTΥ7a TK2=-g+]̌nmb33}ziwcvb6y{6kM\Y% ?;n^W _zG ;[2L/f Ț[kTl; Hf˴c([:s@QK" *$Z,ZǔsQZǔj Q֒@v&Yl&#RJGuOJ%#GcǗbLӘݦw.ƟqM~ҵ.L ~MlXGz=/P\Hhr~bqkf(f^i\$qfc jMY5[5,$m8*k8oZrv9Sr9JeMYOd&o2U2`\鑟|ЁזQkcq4k,PK0c4X{_!Q%\JܸzrxB^Hfxh;  zRXFPKI#J!Ek c Fh{Iۻʍnq hjr-GN-L\nj'X#!Yp޳۸71,mH繠f+ O&Bax;:cmQ-V0/7o+|u=MDi ۔+zZAN-qp"~}6?w1օ'k3Aӵ9-D+u Mn]ŕHpysQ -57/팑0 䓖9<>oq$\1TY%ׁ]xK0QI+4Y̓בV4lbkROGnU3eX;`g9 =4!3G\GBMs" *dZ,ǔj+_/-Q!EPEPEPEPEPEPEPEPEPEPEPEPEPEPo@QF {(HkP%BTDQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@u!E??E"ͯyA\ (?!RQEQEQEQEQEQEQEQEQEQEQEQEQEQEQqЅkBX_*O1?) T$oyNGњoyNGњoyNGњoyNGњoyOF*63*/yMA{GߺwyMA{GfhbxQ'*H.P^eF3c;PdO <Ͻ}?;O <Ͻ}?T֝NpgBMbEE*sŠvr_*Z<KKTHQEQEQEQEQEQEQEQESmd lSM:-= Eg)Ի eKfe1} #g8|TzwlfmJXlZ r sڡѴm[DuԭLYYo @bI]SY{,V=B?:&i]]uB|zt$e9QUd'8!枺3x3gW-3Q0%t䝡Ak5iOjnB….{iGu?֣ , {ƒpHDiwٛm88#jIЇ{[ϧڹq Wm4$O#QR?fotoxx-15.11.1/data/images/file-save1.jpg0000664000175000017500000003110012616075370016467 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:tonemap|resize| 5http://ns.adobe.com/xap/1.0/ 199 318 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((#" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?VR8QW8֠l}`?VO+uoQV6ِ\xOKVz8<9ݒ= n|%XwH F0Gs[^*6';ܾa6V={ 7Ew lZJUXFz+|SCZ[rLuZ/^o3运 ڦeXƒ58e#N#$OXr0?B*; Wm*]5y0Ka<7}ujg7!P >U5]>SNxeƩLm/,%!HC<ژ=X}*}vOӕe$'pUA[֓xfH.yo/H7rH z}sMHwz+Jnqګ:α,k~ΧR{Tx]N2='^_cHW3,r8/ljxvtЛv]!bĎ3I4xv;9,%B#fM}BxO:O2W-(%lx *;ykBѻn+&m{RY eVR[˝AmXqկKw-ԏx̘ UN=@ͩ{Qc9qBQԵK?YNCY\ؠ` -li%ymg1FTSyVC^^^ܘdiaYH{kˁHel8)G@h\ηVt!ykmN{|Jj˨C5g(Ը`I\ک^xn۠ ܫY²d ``8CeB0X "Ϩ۴ X9?'TuOp޹!Kv+H$fWc9u֟$̺洎{'|,ƟӇNN=k/HqksuV\Lѫ9<^ZqlKIog.9go\ScJ9f[]D!Mr?:7nТ멢h1,02s 6hvXCi1t2|q3Q(!xd mn#,qd5Mu kV+2|߃Q[76s@$Q" +2AAލ(VܢBN?u::gڳGpmowC!pżr(zGEr^;kRB#y70w 7mk䵆D~f"clECF+V[kq%cRw1e91*Ğ'̻ԅE70jȑ*v˞sGKIX־ :Ŵa;4QlXpzqU5XyKBJeTFi` =>QKF+\6~|elY+pC g2#E- \:c{P:+VL-'h%?zbl8V얓=\(.(:t>n ( ( ( (xfԼ-oeq1dpA^Vh3N^9_h%xSKBfW,$yDV%fWS2ZsytmV7;נ@Ƕ*56o.k_^l]`;FhUkdɸn+w"3<!{A>=*56o.k_^l]a=%[Iy-mO"sG\Vu&-F+9Uv+2 CTYВ3">00q.o.f!^W\@ 8\ݥzoy,o$d>h!qNORq֋NQ7ymP)23cd\UI^=WuW"Βr޺?3ޛ MuI&`ۊ#_J7gvc\fq;Fm9m%QKX@v1,q&K i64u緞%kVi{rJpȏng" $V.[i1|Fqȥ.z(s./_{Qy|y"G2(/gi*x~W7M/™LʌJtVNo\=rjEܛpsCu1^gc=Ti7QI wsmLT7riKZ5 z;;əD1I):uӰu^^[[*aS,S3R+LX j 눞 .9FPe7yZ]Y#n% -"j`g?.8EpNw1\W{FS4&h#6Oů\F[-;jd' y1X^{ Z^q=[P@=+ѵ#kGv7'Fي1&,!Wp*z&(y/bĺ{1,v.JSP> Mͺs3Y2G,+ (? _ש)%dt ( (o|6EragVTլ*6e.pDhσP/{O\$M8k6`5?_&iAO? 'G4[^9_&^%j4iAO?'@hC\%ims5cJyy?? W ?_&b%j4PI!!&P.ň?*q 2ϖ}AT>>i*(',/-i-%*?CR[ԥ5'/gғ3Ji*` M Z0(RA(p11JN#%%*z`SdS)h)fZ(gU _MU6JȑF3zzզ %((/ZnhlOi֫hmѬ o/@:3in? /[ps<;s6[Fs> C$9+2;;i3C5ԲO)yВv(*PM~-J+gR[5 F:=jqލBك8|+go [ap.2Fy\` H)z췲G.. Lg%T(`yS;:mB8T2'<[ ,chK"b}ۀr}}""9ŬK#y:Vƫa5Ŗ?.!N<4j]=qWt9^+RSiEݽɑ_a8;h$cRRJi6kڣZإ73Ԟ@*2fZΫu&6!zuwm̑ebW'WxS?gon+>֮efh-$xS @9ZjM^Cl-fwrH`̥sP3m3]Mi_\g6dh㼸0 #1T$o"ͷ!dpHcLJ5VK!IpaI2V #ݐ1\~ ( v]m\8wE`X 5Xm85Nq%q&xwF׬n,Nx#=:qN+EhscWOwk6f.;X%HY +OUmӤ2Zf_1X*rWV&syl?VC2 @ U$o]i sKHn m˅ @$ſK"@WV7. Ic{s;s=xy3YH'ݷlْY.cXXb#8`A<x{$ |c&~\U73VZQlp3b?Ͽ=Zڣ 1BSVÕ8רjmbw67pD$OR. +] Z]ׅ䳺?^T 6']{i eQ@- pcH%`Tduvݿ7f)mտomՁ??UG?{h][F[W{h [uoiu_i]f)mտomտowfGkop. 䱳ة3ד?*񝦡{:j%"Gj̿~fJ5rٚRMN;l$r,kmJ-B!D1NW$ 7kV0^[~fYx\[} 0W|zKLOGx:x:~ʕrbq3Ϟv)rOZfG9LfE3fG}G?{h}#4)4~?H3G}#>g}#4)4~?~?ҏ3#!1uQ@ KIE.h%4PQ@ 3IE.h%4PQ@ 3IE.h%4PQ@ 3IE.h%WYbd'8J*8mGoKY#R+.[WQ .{[XU 8F;nRvm_>lo2\sp@o#R(u/K/²,<_[Msg-O ,#|)6#/mlXt .{/Qր5:qefoVw9c匀Fr={KSj!ӭ]ctqHT^oGo*/2j(%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ *ffކhǻ[5մpW1,{P{zοc+$m _0qUr%77 䡐0iB$/{AK/C"z?0 6 oöf)!3uW`sF4N]Mm.`(rPr:\ȵ 'ȵ '-a{}f_@*LU`s=%{յ6yv$IF2}hSEJ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?fotoxx-15.11.1/data/images/batch-add-tags.jpg0000664000175000017500000011067112616075370017311 0ustar micomicoJFIFDExifMM*bj(1ri%gnome-screenshot0230ґ+01002015:07:02 14:10:30Fotoxx:resize|tonemap| Fotoxx:paint_clone|NE http://ns.adobe.com/xap/1.0/ 428 500 0 C     C   w" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?,8#o:0}yh}Ǔ$QyMk=RkhYm|7⏈>({3NHm|ϳ?&߻]w7IF6gYB)Y#2QԭVբ6fݻ3]h#Wf]_>*4F f^zǤie͚ -+ ÷ (<~UA:kmJe#Z9#L:,oc-G{W̫\Ʃq-Z"ȰI*wm]6_\׋Ecg SkI}.MGkLCeVy>ǥũF4f"e5"/cVlڮJodi![~oUkW~j9՗{ZԼGih?Xͬx|Vl߼{m{HxL6ze_CE.+Ǚ3/,De'SO7ѧ ާgb[o:O&?9|>.7߉2׭=&Vk]x $w[VFnڴ߈h ռeWT_)|o3o~#k_|M4ZK&96d5o/ݿ&_j_ؿ_k3}_<{mSRKkɭ 5K=.ծ.-mO;,k}5|cG8fYo76ٲw[Y7.֪<}oGmKJ7O˖̻ѷn]mB7)6>xbeVh7$VGu'+j`+Gڗ|aGsϵ/SZ>ԿOkk>ֿޣ,n}+Xk_QcsKV/SZZz j_}}G0XRu?KVֿޣk9ڗ|j_k_Q7>ԿOhRu??Zz`+Gڗ|aGsϵ/SZ>ԿOkk>ֿޣ,n}+Xk_QcsKVܡu?OZz`~59(J+&1/TRf2X[k+.Z|𾳪Iyyv[xvZrv?W NmR =7MPK"f$|3|Y W¿ض_ۿvT1+Gxه9>"_ź:3S!8c[U_nw~=nֹ=3UԗodߺT?/h1+G,?;W EM./C寒˵auV%;[-+ݭle]ۭo_ź;ʇx|gx|y.~ԯ 'ºny} ++Iy[*-&/٬6|\+O־|T?/hay>#uo 隄:\+of7WЫKVφGEem=o^x/h1+G sKG#5 麆+-fu%lo#Ynhf+.Z>|g}V@|9'EhZ|g}Vfhvg}V>+GcZ?Vo>+Gxه1h+]xϼ_ØVϼ_x/haq?+Gmx/h|9'EhZ|7ȇ}Vf w~REwFX{0g:=&ԕVo-UZQ>=e󼷺V7;ZC1Z7+FP`y8*oxKAu-?O,n.g'+{xM#:jvˡ3kz[MU rMlۛo\ak{kMmq V]VI,a5fGE >~'O5Gǎ($~\~r pȲ74{~_k*cգ~8VMyr-h~옡ET~᷊u^ gG}Ort]x YgO.&5ɼ . ʲk2UojoOw?GXCZ .d[΅>γmܿ{gM[wNuivYu2?2]"f3wjz3o[kzFUfkf1zjEmK=u9'l?wf-|s7=[]CW5/I&mx]V;8v!ۻoʿnQz\v<,_Ig]KtCN &K֭o%Ռqq mF_-x ~kYǣ3OI"ʻ^[diXj^,icHU|['XeYk f]>?9Ὰ *՗rܱ.k?Aqz3^-Ωq5*$~dk&.H|Dzf]Ecq]-rn4_'"o}"ojq#ih*gۭ嵌,VK5f毌~n}TFGq$>bk-Z?vFݻk?ƖW4nϪ|B|Mgiw*g8٨NG'QzZZbGnϻo'_|WĞ?[^|Wk᳣Zϡ_\ԍ=i>б#Hɵ~b|p ռEa\x{EɦVI52I7ݺ.iF?{'j*晶 (Š((((((((((((((Z俾yM+m]̿ݮjDڀ9O&u/~۵/WH"5qBϊ??9Mm>?9]ޣoP6:PY(_H+wy+Z5oQqw,|QcWAkP+}j^(6+pʳOyqǻڪ͹U#ofPRG]/j*"֧}OfyS~ۙjmbr<#EH,{Zj}?d4}G՗m>ڟ V#S?o5M՗o>ڟ VȫQ>em[϶7Gշj+ty}%RڶmOw_nWuvo>ڟ 7n}u?EP۷]Ow_nWuCn}u??S] UWu۷]Ow_n/T?S]n}u?EP۷]Ow_nWuCn}u??S] UWu۷]Ow_n/T?S]n}u?EP۷]Ow_nWuCn}u??S] UWu۷]Ow_n/T?S]n}u?ER۷]Ow_nTvڰE3B*Yktۿ:_h|ne'%w6U]kyk>zi-݅i>λMYWnK x]?QKEWmqmdrIv\ NùnG"eVha xS@ xkO+iw2w|UQR'뚄6?g[?Mi-kV6vm?>%sk~3.WIIlYUe>-~^$M?WM/TKkJMJ; cVo,WnZr܏zǵYv^ ;n-[[w6Y9x#g| bT駱&[o*]3 3\4{⎟+\7QCeo5t^MXR,~E䛷7s}O.h<@ڶ_iIɥ /}W1>4zo|3C/]떶6mi-m̵ډ|'=;UegcqyvkMeo$[ˑ%4r x 𮛧iZ7/VV]m^eMW-WMPUI &<;ko>m5)o6207cknoD~|Mxv [WZ.KK$aۘ7*fVյ'e#Gk մ Z;K$horUzwEWZUiwWΉfkbo^ mBi?mpEѐy։al\U_/'x<>;Xt}:oV^#E2:Mt-ݿ7][Mu/+oͥX5)0ym_,tٳផ6>խ(n7h?| xvn`a nKxlkJgMljm]Mp[I4~\-UY7miZB|[}"ZT^<_yHo">d-"h*MwW|EI5 X]k~dr!Wf1~f]b/ KI@d$mӯ=/mo:|1v_|CwŴzk;[HF~б=jO#C^\i_鬾gǶH#_]jO3{2ԋ.U_2f]wUoO#vVN\G%ʪ3}eUoQEQEQEQEQEQEQEQEQEQEQEQEQEx@QC] s5]c:J(ƭgxz/4kڭg=ֵmyZ%7?ڎAoL]:7݉|3r|]}V4c)hzn 8ڲǚG>oHI2k}Øg-reC2&֎MyYk>"GiWZ%͹aiYWk_⥈S,cTJ\К=r⯅L?2𮳪Yj74ѴEfXn߼U#EmKZr5կ`5]{^uo=Qt{ 9i#/~f+ tItKyAoE?s73_i([彅eZ66HYo>mmZ*-;nk!>u[;i$lj[vڹ5u"a)J?חQ/'O}S:?|߈1bhJfk2} $|neWi~,OE^ڤ]X+41ŷK9`_TN3G"ڟ\}W?juqk8OE?:4-O?@}?Wf([S>3G"ڟ\mO?juqk8OE?:4-O?@}?Wf([S>3G"ڟ\mO?juqk8OE?:4-O?@}?Wf([S>3G"ڟ\mO?juqk8OE?:4ٷ:0Y4[X|5niWh~-w@qCP E:J(,W|^m kiiĊEs47 uo̭zB7jƭ xL**{ZR>F MJldYVd_H3m/hz_43Zt5{?!߶B7j8zxx&]l\H/ ;ԭ=ved5pfk?']KOtXʿdZolktͻܿwo^Ї~ںq3|,\*_7tueu&tەvUx~25Uo2{4-Va߶B7j@a1m%oHۛRVmZx{IĖ }6ٵ{vPhylۙUV?!߶B7jbKX5b/ K\,%ۮkJSCvtņʹUmlvۚ5gs}Ї~ڏվx^M]K:U鱬6~}WԸ_I]]kOzBuVЇ~ڀ0Vx-x__ PmΎPekWjMVGºWIΡa[-ٚkmm.yS!߶|&z7Mk?B7j?!߶F&79??5ѿ!?ohCm@XF'G&79?Ї~ڏ hF'YyQcC]?Mk?B7j?!߶,hkC]?MghCmG?oMtor4kCЇ~ڀ ?O&Mtor5yP4?5ѿ! ?O&!߶B7jƇ&79??5ѿ!?ohCm@XF'G&79?Ї~ڏ hF'YyQcC]?Mk?B7j?!߶,hkC]?MghCmG?oMtor5ZWIլvq7:KyP&j)zQ@nu #iUWs3W?UOh[Yj'򭺀: n$ʅvXUҷݮMZMѵh,r4?6m@Z{[}4@kh5/ ,oէ'mvV=on5<9gy O[$r/VVoFtqn9Yr*VwRvtxgT$lE\ྍ_f6Qկmt6mCROwIsw"kfVkŬ"nVV@ IXm;j_˹W妴L@i&W4j$?/QwLOԊ]UKݻyZZ @?k?Ou}_ޔ}($&}W_nIU=MGڛҀ9IU=k?OuSzQGk?Ou}_ޔ}(}_?&}W_n7joJ&}W_nIU=MGڛҀ9IU=k?OuSzQGk?Ou}_ޔ}(}_?&}W_n7joJ&}W_nIU=MGڛҀ9IU=k?OuSzQGk?Ou}_ޔ}(}_?&}W_n7joJ&}W_nIU=MGڛҀ9IU=k?OuSzQGk?Ou}_ooL 긠7kU_7W,+]IYfYv_ᮡd\UĪT;{l[j(j( 3iwʪo"_뚦Csh(ACZ8VfekIWth^칁>/_>'^?;Hut,WjT ~x~ŶpkRY/no6]N>D|)|Y3P\I:2'ެ >&[Io|}ǀƓ^}gN_ h ՚Oj*Yk2k韛vP݌_}oaJ4}66y#LkK} ix}o.}ƖחVeO3k/۫M{A|QiziKkiwm5G߀,-|5ILHS;[Q!i:^Cfl˶9?~| ݟo~xKnW]5cixyg毩>ݤb|$ G}6:VhtKmm۶V_[/`!lsc6uK}Kly_hwnH~3^xAޥy; m7PZ VOkcAIm97s4 zc\mm_Nx?sCxÖzj;Vܿ.ڟ"ګ?1_1x?y|U:hg[{e̒5VEY֗쇭$1 אbItFlnfUEUگ-M6>h2V]-Qthù~UyDUo# ̗*6-v2$6 ydZWP8Vh[ơbl9$ˎ8ٛjH۾m5RWv&>%|(4V|m$I]-wY$cWo@aPY/2Oھ__:j_v4Hcn\^4խ&4OFeYU]+vSe?hͶvҖ8c۶ȭԖאQ^6߽7PjTzM\_x{;q챯'ÿ\1:,:i5{|/?wU~1FW~c5w$z,B,%2z ܹ[ Xi^6KU/jY۷OJxsUA=kVjIGi?2yZU~/~(|[uƟK?YeT ƍ'˹UZmŵ7n6f_2K{oUeǪq-?dnݚK_{Nu\| ΣV l5.఻ӷ2LѲвV٬{ϊn2mFItKm>km:37Dl,CnVf=[_߻7X>,ާ^^dvcO[tkkCET^8<2lJMRg4Us7kYq/XŨZîY^C;.o37Ұs^oikwW "Uܫf$i⹖3Z^M{_ѴYxBMA]k6kXͮ&e?2E۹d_~l5x~5N.-nY5+E즅6Ͱݵvy{?_d?l$mjy;o۷ݷnGgiFzݕחcygc9dɆ6߷oz _ٟºw RˍsI{>>U/Ҽ'썦^ZzsjMyoUGW55OEfI4ɚ[=KMVhK.\:Uzc>"Ymn|٥XՒI<]̿u[m263OGubI7±#ڋsnܫ~SZVvn.fS){ 5̖,3ENvQ]h+&73Yi/HFh"C|DoMZ\zI\Y]Dn~eڼ7Ggj#:ɉ_^]<*ƸUco˜LoL)E 2by,^5E sI6>tnD6Y$>i7~WhV]lBES5X9岰fXWo>Z\Fkk;Iey#߶c4mǷv?O9Kw4;M3Qky.iƻ]PSԠkCo!yn[t[k ϵ%uui6kkE mi#۾eͶ|)'oxm~5S8|e[xUڿwjSQJxS^>&OA6ѧc[v9?6k+cceHS͐ρqiHAIP85u?1nFvfvд}cAU{˭m7kctڂ[XV5WVV_-k7Z%OiZE|k6j|Hy5bhv$KdYEvW+qOVh:Me%^۟"ZKuY!e.w/0=FAv!o+.u|f߇Zƿ`sh#*PѮY5efڬ[-Yα4 -M\\\M&>_W?xBFx&hveY7G"ȸܭJD7CX5x.5i$y'˵f@\ir>;[9/$U6Vm2۹k-}d3Ȼ]YfH[WF ";M>q_};YCf~eܴu@˰~\x_VŬﯬAmv*͵w_w՟5-Z)O:3K ͵?V+~z?fcҮ-N ;J`xk#m|fokS :h"y.&fm͵m_ݦk5/vI4;x\a$+ *mmʬۛl|^m.ҔP7sYnc+_#l++i? ^g _}j&C5ϵ[r̿2_m=­"QYUUcEi~-^o#̬?hi-t}}WRJn}\HO[m}|.]G~/Aҥ 24[M$mjy~?u]e,ckF]Co3Moݶ6mUZW'$>\Af}Blns46_,ƞqv }um$w9J`v꼭>{ ^/ԛQԼ綽~4ͺI۲WX[+(-GE!65Ph(`@1C[ڰ|o J䢉(,gzx趮ώ,Dt)].Uh]v[wWqmZ o X^$t{ۅ PI"ەVoiG/]o?ƢŃKˇv k$m/̫n۶xc%\յk^O%?77.]dݺ |Z}w◊}>k޵ͼ_ շޮKrPӼY_= FVT}G|(mKkǹocnEfcUm]TZmo¢8|?\477Zh-Z_x|iwyVϪ@ A1WehmOi \*-Ċ~,{7*f#V7^is,ޅC*f.udGs5ޠ"s5*mSk+Ѽ-skz ,1KpHuFR(עFӬ+]jJe.FyI"xZ Rm:OVgf ڻr6Sͼ;ž #ZҠ+m[mJdd~oG𕤖qxF{I-V[T+nUhovUL|m˟Iu6Mz%&qʎ4{WC@ J.4mDi#R!f_mf^*e_ZH_mIzGMy{mPKLgO=Z9Fw{}B)?1ǻk|[⇃;(T^Jdh|F_gT\Y_k#rލw|P׃".Wz4鰵ߕB7|7RƊO> ׼m> r7fߵdguuW_Csm2O$6uoi 4V3^^\Eii4;W33tZ_ MΧ f4yѬ ;Z[ՅmF^ݻjAZ[jRO2KZ$.uO" Y<-MG$]M:,W.cǙ wvwu_WMfSҵM麁_.EYۻk|7sMx/9u-RԬ|^9ҧՖDhV|DkM_ֆ/5{kgiך>xyw|5~joݫϾ+/ kzJi6XA\&klK}va PMR]CMRR;)<e3/C|J<*On Uai||Ͷ[Vf?o_ x֛ZE[ͧ5¬{w\nVO>]6J=ծdWtl#vݿ6xoO o{hMۥ>\7w }k37yci:Ǥӳ(ƫW!US5-3=w^ K[=>CmIHim]ݯFA\O▘6+6o^O|Sh/|]xr~uu=-m"cs|/ojO^5qbOҵK%\G,l/#^݋>g͉dԞbz]nົ6hṸhcfU-I ?Ï:u4&Tdl{|36+]nxkZk:j-k/˷j;mgB]{mj}IiѲ1k7oҼs xMD[?I}WPs3Gfݻ^*']@g,yi4 HWw^o3񕎫}sYۥU+&fX-M42y3GmY~]ڏ@t;$}Yy5=& 7Mc14͹VMn~f_ImF[\irXٴQnbnX|-vȬ2y/xko%yKaZ{u<_4i,7 2~wjͷeLm /'-2k$>_ܬ{wZ>+/VWEѬ.cswI&k_VW𻯂:Ŀt A-&h㝖6^Wwm53_ +eufM3MHx_CcyuyoakjeVIc]mm}^Q|it"?2ZմVԶCZIY<>Vܿ*m GФWv<_?[4 Pe[Pkcm mm+/__|A|f'Ety+y9|ټ'w.Y?3~ZujcJI,MѳPYui8=yCRBhٵ5=&K5V_:9cX}k|w#Q-uŕȺکi5dZO-}ߗo^;hi}sMk2tЬj*avnWKѭ_g 9Rmafi y]joa \1 U_+YiAdjf7ڌkacYduXwBq'ٙJZ>'kz]!-!/&cř!Y>]f>K|ɱ%f!g|#]hn_+z],+6VU~V۶[ZkiWMZI8ge K4ѪܿOiFe}ᨮ5 (5]2,dmʪT.oc;[Z+>P-,\42y/bSV5-5txA׼@mj rqk"ڬ,ܻ_Յf[Fկ4 6ՠԞuXe[3n&o|;YWվᴙ3ɹ_U~Zh6 -ޣechTwW ~cyKI/?tJ/ 5M_ºMb4V9nS#/6W%']RJY!DiqiwIv3ڵƽ?Qm:{xkF}ݴSR( ( ( ( (X>7 cPv?Ҩh9(J* 2|Y"^7-O_ /i_&~g=$2me$2 ş(qڼGԼii浯iR%co!XվV/GgIKWSXY@j%: g}z-B;먯5KE {tkWi~7w$kRC6=.O٣vݾgTo=rέ]jI%i$Zǧ$̬5yj-_Gqimt Ci^\_n5[y#kI2FgEx{UQ4X2ډY;ۻ'%,jGhnc,k$l~n->3YZ_4A`ZYF摛vn}|Þ|+~ծ\ٚvwUʾUaHŅ<7[Ȫ7FuʱWZ|}k>CcG6z`L{~[J-SSq,ZŌq5rHݿ/I[hm5W5t}ҳmVݷ~WGgjzUzԡ[Qrq#F9wmE4}szw;_2hInFVM7kO9{Vw6}>ٗjfM#NbJgw337?3zx'~)ii_-Ja[/eomci$ʿ,m^7 O \[r귖gmڂhEl032YxP- R+۩cFow2/OMo:0y]: SIG$ǻj+i1@h/gd:5&5s|ʼnbxgQe5fvYc>5v%Gͻlۛw^03iy5W6gaO5 oEZAmq4ǟ,j $rs"k.so&Xc5I*xW'uM^ffomo36-{y^h+mo6JuoEEյKPԦedY#U,Uܵ ռ]^RdShjWk(Uo⫚ƿZԚ>j)g0F˙6ʿv{x~כR8O.K;{!wn$jZ?,[C*fi3n_]k'>M׉Y7H,$+k-z}e=ܢC4#Ffڣ<*{=+ßt{{دbho^mdmѶ*9s$jnU~eZ7 cPrQDTexEs-ş(m]QEdQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@v?Ҩky;VXT4Q%^,G\}jg:?[WuDE!QEY!EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPNՃ_?U G%IEAfW?Q?qڻ|Y"`QHeQVHQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE'jc*`@1C@EQPYu趮_ȣ>EwTDREUQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE ڰ|o JX>7 cPrQDTexEs-ş(m]QEdQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@v?Ҩky;VXT4Q%>,G\Y6Ymky!˶&Rq,Y-gC[qB-л_=VImmsл_=G%.PE[qB-л_=@6; m [qB-t[h\%.Q m mmsл_=G%.PE[qB-л_=@6; m [qB-t[h\%.Q m mmsл_=G%.PE[qB-л_=@6; m [qB-t[h\%.Q m mmsл_=G%.PE[qB-л_=@6; m [qB-t[h\%.Q m mmsл_=G%.PE[qB-л_=@6; m [qB-t[h\%.Q m mmsл_=G%.PE[qB-л_=@6; m [qB-t[h\%.Q m ?_?U 3ۏu?kǪM%iWX_̲c呏(&u]4_٪?&oh_?&&o<_?&&o<_?&&o<_?&&o<_?&&o<_?&&o<_?&&o<_?&&o<_?&&o<_?&&o<_?&&o<_?&&o<_?&&oj3\ >Do/̑wmUVf4gț&ch4/JUg٬߻?GMf%chBg='"6k_a~7K|i_?& 5g]٬߻?G9Mf%cho٭߻?Gc=f%ch&ɿ?YvBBg="oț&cj-ueϧ]\-XV)s/ͷDyk-koXI>fڪ_o@?f%ch4/G"w_7k?2k=M?14}oBk=]٭߻?G|oM]٬߻?G Yv@ț&ciL/Uе6u$hw\̲ym"U]coj\Bl (./dw*#6cjM?14}o>sJux؎='"_6k?4/G٦OD.lֿݟ# z/٦y~ #u;Ca/<;g#2+LѶ|?׎<|E%LO:6c[vF~Z:?xu\ ָ[/y٬߅߭ K{䙬Tdwoט/:GC~EյjO/l fϕ/yj^:43WڕEIuZ}|o_ t, -^[mf _ۉ~Yk/ k q 6Xo"zƗ~ #7.?2$r4k 2[㵃ði:~ ApA.6+E͹W#Οcx>ldMݻsBB[kM3+xsFn%Xog=*_'^6/uiumg魩ċPkv<[2fQ|2cŚuZn?*¬~]۫O<"'nvݷmsZ!gRoቖŖ8~+GG\Ǔ~xM*o Z]ck,l/̿/>_;*Ҿ>Rm*WZz}o.?wfܟu㿆?}X}M=Z=e_KcğhZ۷jnNO-Hw9]!Q ѓVN4pn>[,r6o/TkݯE;5\\rG#2;#CKۯ2&Z ֏[xmMcnko69-پmͷjw|`6hrhkhm {ЫGWoWMotk;Z֡'y=ռr\I[o'g}Spj7}Zڛ|߼o6<*u޹E=[+unۅ}[hx/S|7kn[V6vץ_ &K6z]ucY WoWΓo%m٤Oehaf̱աxkBKkuo]rhikFy7=A|f',|AF&l#H+n۷e۷_BOY=G{W˳[u{R_:۵)<}yW7-g$W|N/ GK}YվOxORd/4:,:MzZIu5ͧ!2F/˻w⻑bV#EyyQ 6H{_ѼQ7EgӯVɯ.<]w?ݬoE/~MGT}5~s.YW|I|5v=j?`$oOY_ u_GoY']u٢;Xy?iO麗4t[}魮`I[դUeeZl~7Y_xoF9uo iK$+#4-vݵ~V м ;9[WvQc[ խvI,hvݲ7̵D ~~5f=KOHbEkab&3Mk>\4Cޙl>W wW+?2eo<=Xi,KVX4V8fYfqjoEEA( ( ( ( ( ( ( ( ( ( ( ( (i(=h袀>>(袀>>(袀ehE}}PE5g+lwϢ<<(ϡffotoxx-15.11.1/data/images/mashup4.jpg0000644000175000017500000002324012616075370016120 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|sharpen| 4http://ns.adobe.com/xap/1.0/ 170 277 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?E*MW}Zn} |9`E<%ygx@y;0ojoc%@TbܤEq+gK;ڼ֬٣3!`!ZSN=A{׶@J cw=N*̍($6 )w?!Y|%!/.cS{j~:qw?!A2 .n|j.ermV+8' }w4O!ӯ0m.#;|pމK{~B WWm!mm8 _g`W9F;9a}RW"ϛ(V2LIt~BOW RlR;>[b jPg }UxOŦ/,,RI6>UY1s@ 7?!\FJ3Dv@w99ϵ>UYA6KѢz7?!H%V pp&Ze徘.tK,qA#Yͫ]iIF5޲ /%XN8=ORVeRbqZ g[I+a;{Nx ]vz]whYb lٿjM?RN;?~QqnF+2]YNJ~TzʢT=$iN̅"C v=jq?*8RNNBpI'4'Ǡ~T+Kgc՘K @h?iX PGF+xc2ʠO"4&htXO햟}~ :xBn@_dB}2MXKIm2dŎb~i?KG-?hb4O,)$蝆J7㳱hVF_{eA}-l?北2LI1ɐH-cɬy1} kXcذ%=QZ~i?KG-?h L˝3K.{$Bt Tkx`8Ҭ~ WG]Q[+8 Ry\n46\iν$2ճǠ~T2^Mo2Q3~qQ3OY.ga%zʋGѬ4[1Dǯ^$zN 2isJ3Ʒ8zʋ̋;J1/YSp_/lR,!0&c=P{VAQ`>[msFc[whg`S* Zzʎ=Ef1xTcԁWC** ڪGǠ~TX.dEkqX.Sk n5 wX;Ol[_X\;Cڲ]_jtVi)1(_ ǥbϡk a6TW|ne?,:mMv8ۢMPu8줵᣾GxT!qgr `i7kTiFcgY-($d6u3Ay1y<7E՜6-a%M W{Z~ /4k,݈XI%qpFh sėֱJܩHT.⿖@8My#8Z .wo<7w6B1;ˌ aQnuQWv|֯Vda/Lu w[Ѝҹ[mWMAeqV nA,?A2N*(-Ǔh#5~5mGcެ☣ֺl>g܏}}bO ~nεey683u9*ޑ v̬8(gܟhGf=E6--KxDpMX#bY3\kGO3 K,ea84ֆ85(Qı`gxKmoʢ#ڵnGL-ݓA*-W;EaR\3i0$9=GZ諘k^q`J20[PmB*Uf5<]{r@4Ƞ3]k_Tg,ۗ*I\{i=iVc9@_\pzt&_ Sw Œ'sN /?mG;No {}kT4/]cm%xt6}ĶX0p=9w#7p I[Fsl>ׅaמW_C;Zvڙ'0K 3{?nc,Axد=An} =2Uu侎+q\eH_CH;['NMX}hѣu+d# =7bд=^ïym [`|`<Ԝz PeERQEQEQEQEWޒW|d7BQ)`I2MJ`$$U)8R[>B h–J)l77-E'kgN@ޝHK1OANdv]ϲG.?Tp fihp~ _?Kg.|07ӀGhdªh<_&?Kg.u>MWM6J)^qsN4h<_&t#X)cI#aGPA0;J; VFdOdU?`w_ /G%hI-EViDAudFB@f ((((Z%ΛEZilKw,nV H6Oks{P`\1eq4BXw$6IO 5j#Oi -éyO]IFzYb7  ixC%, X$fcÐrTwlcި{+NJqʒ9I5`Ls$-#Ffw1!\׿9-oE=I4+N #浛xZ?خK1E|XdOִfqu*~@̈aOksw>j7I1dg+珗9 ,JDӋ,d+. <Ӡk[8P^"K7j<"3_;$`lɰryԟ˜j%tk[ u+ˣnJŏJG.9\:$e-ɘ!!p#Ca Ud𣵙-S˜jRSi]Q1@ϰeԚ5><޵̀U;W5 6U{+dH2`h^˛*4/dڣ#cǭmj,v:0!L}MIp;1OZJ(Š((((((j~I]lT}SOK1y&=bOiXO]{n' c' SxG?5+xԂHw>7G; 2HX7c)^,b an g2mCgrFn t.3$TK)&.hHѹ H?iuC~F.?4diy7E?w]?u t>Ȝ֝ϢGhM?]#z93#OD_I_Q@nw?߃'ƠoL86ep*y?:$|Q6G&<;8lYUduJ b= (aEPEPEPEPEPEP|=6yckpY ̟2oA>{YNG#"+Lz].)-$&9&c;󤰗}:;;'E/<?0tWPZޛB,DJ:sҜnqϭ?ƀ!b:\0sV(}M{`jz|X_>`OGs\t]v BYu"L/-uk?q|g}n?4?֢Uhp,`pxBrI(0(((Nq(Nq(Nq(5:nƣqS?j75:nƣqS?j75:nƣqS?j75$,Qp&G?ޓ]FƤw`HL֗۠ԴX_|361#kO/gaom esYJzqkc#G`\o~Pxh1((((((((((((FUu*X`Њ{IrglQE/"_4Q@"_ӣ'*d>Q@QEfotoxx-15.11.1/data/images/arrow31.png0000664000175000017500000000131412616075370016041 0ustar micomicoPNG  IHDRJLIDATx^Ka@*˲01656R "MTs6[C:+{s&5Tq6SI$O5K%#m(8t$g鮟ýnSܓ"uH wiu*fX&|z\F F]_Lѭ|xjYZ_Lȴ"3ӗ;o`Mf2,=(VT`/<4AE12  ɼ ɼ9!]aKZk._]_sHEn4/"p`C(80 EO;du,yKAM;5Cd]B.~CXP4B{f嬶wS Q%85<%ha2 gЧn?}/4} cNy2(>SZt~Oͭr{PE x?wCg?y +kr¶QB1NtnUd|eI+k)¶87*ѹM$#9jY(b yհ[r"#MbN.q)S#hz֯.upvDZ‰'[& ysh f76 IEun:4Gv"{?{C<äIENDB`fotoxx-15.11.1/data/images/directed-blur.jpg0000664000175000017500000002601012616075370017264 0ustar micomicoJFIF0ExifMM*bj(1ri%gnome-screenshot0230ґ01002015:03:22 14:08:28Fotoxx:resize|tonemap|NE http://ns.adobe.com/xap/1.0/ 173 206 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?7IӬ$h叩OZ]wUnheuʙ׈/# ndőzwrbq^ͮ H(.a7DQ҆Cm s^yV[I)L]x-bhmgFȤ2sҮeZxz4tfSku. 2),z&j5*21{uڥI$2ek2 U2dОխ{"rFS=73O=[P.D?=j3EƆ:/^y{1cgX{vlmu=COvu.O 6$\YǢTǢBO>ڼVM- 2]Z0)AԵ}Rr:<m9h;ǢBx_ʼR/_j 9&#pdZ:xSMQM>}JUJcT*.9bzc҃E*9` SWj+i&5D\H1n֧bףWXk$1wsacMFdh }+sZU"$"A5imR$~ ;>o<E]+- 6^&S)o<G+c˨#s-Q.* }*3G7& c W[;>o<E]UHGb8^SH:($R۬r4 Fmn]o<G+ ;ͦM/>;F#/xO4H^o{V> CfLsBmH|ӄ Zq[#GMi9<ď 0 r8jי,Ox +BgZ+GK"7 7'{~U acS\π5;3^]k$%V=þIJ*SbMyܣ1>hèLJ܅|בՍ7Ǟԯmm,EF'\Hk;]1kWc?wn6}} Ma'&X}L3)%~/y]dibU YW*xw6ڏs4Mn+)|Em^i׹ &sc=sO6LҮ|MX˞G[\v~0ouq[j e5[v^FG5zZHt] Ή'9W|7}/^7OgotQI'9=q[^-Xze6A-yszVO?<= _;E|-vp vTQ4;hg ̂C[Iް~޷m2%Hbpg(UCN>]%*װxN]_RIuhTxFBHg͟AC@[tU+ N) VU HVn`K)mL"8ܠX{MCnѴ)1hA$ .}^DsiL6'Cx/?o'`(ђY{Эtlz6qíVLPseT!%U* g<uqV7oq 9S $g? n-5V80bF}?Cִvt4H7ʲ20XV3@𦧢Z-"{Ş'a"3]M K#\n dd4.h U@@F(b bP3F(RD&Q4ڍ1mKU @ *%.SHCۣF?G߆9Sc̾t}Fg]Er'\uo4{"ACC2*YOʖ^Bc{dOKScG[kK˱.QTcG[iFC+u >[(/TRM$ܷEVP`;CY9ux~$啀^M;Q$h5$r MnOz}iZ[$oxÅoQ2O#oHQK[@.m#g9~ Bًlp3޽4])-"M2mMB :0>4Ky&8mйaѷc9]O<%ω5Śt:KQlPG5]o,? a57*-lmvC,4 ds՛OMƠeAYi0f <|{&pw Zibf~u>?]iFl}$qxw~syu4vsKw[xwSxF4f7m%3t\tl9t>4 \rY=ޯ&bXJvkOZ6 m78G@r;b }\yW$]:R?JϼT$[Y "3ʕ`zdzVs_Cz;{Yo/t֩x&7-Z[(E4;8<$Mxw/n%>{@ų0}NyVۮ{EQt i<3cӴH/$oH2`p35鑢iik >(qk2&U 럟$pǻp_jP/yHwg"kUG\|4- B3^%s#o\WYowj-I/-?Wo2,Mb=.m-4Ӕݵ@? ft,l-VI`@]>[{-fT1Y]H԰a DFU@vQ̃]ơGmShO!̽WƧ51ݕ=_QKQ\\Amx"ysg v(@ bk xymRg\Q_"iq-끚 kFV5i%ͬR\]43 "~u7'E"/("1?gwRT=gO'Egȋx| <@?d: C-w=?ޢ9sG1`YzTU^'Es21Wxo2$p} vb]lqɱ.ihɴxwW:MguKp3T4mST,q=nn"HS-?}3a{(@1qῼ=EIEY=/C5SRWGt/ VVD㓌[[ֲ GP\f%,,6,V7 GLS7Z#5|=y:<vg UwXxYu֖ŠOw3qFv걶 q ݽ}KIPw/mnb|=֯|GkjN=[(j# T]pzmu".zsVnc󭦈ute)_A+22'8>i9;vq5M;Γ>Ɩ]CJ&k#qV#f0"\G/5/V@m~z{֦1iZ]SLU `U~uVi`ܗ$lIڀ'4ţ$o}S<Rƀ1L4yK~(yK<>g}_WM󨼥Rƀ%?󨼥Rƀ%?󨼥Rƀ%?󨼥Rƀ%?󨼥Rƀ%?!$5}_WE3_Wӱ?QE(%|I*QL ? ?"ھ n.5b8gI.KyO$]wRO۵Rhk-#}lzwl__ȿ5|J֬R]YmNB@6P׼a]GZڵ.Sdf0qy^ڛKIn!bbہ?*|C$[k%BVvrea[>խGA?ɿ6wW& o=c('mS"_#z{OR]Mk(xTK 6+ׯu[wOG{cAe7RKȿ4ٱ{kYi,UL]B-턒篵ikgzVW3Yj9=i?밖ק4n.59rGϋu}sPա$O~_^Wq?u77eZ'qs"8$s2/wME1VH>~@I^ki.'5_j -|*#[=?2/uM'l_k_w+(絻Ӯ%4e s3OHie]Ok,c$H҅=M͋{K3^uxX5}zYmj=5FP9ҹsƽM~UKhbOցn`6fi6pAMRt7UM; .QE(0ouu ";2 W2XbbhA e.-EdX&'t u|:h$0Dߓךa>VKeh6"ag dZ =Ѵ k[h42;{T@q޴l?/Qa>V€)I -!, QAei]h׫Q!"*2fK[v ##SI6 i֚}R:B:fm[#ziٶk~(6[\[æqp12,hAiKq,M,B|.Z1I=mZߥ ?l?/P-6EGӭ2}ULqW4'ܖR}FNK $ ЈUv׷QK":ʛ7 xjZX|0(zִE24}A?l?/P|z6M?L[!J#MVϯI ڻIUQ3ue&fϕ_6|RL!+-9.dQ8=A=Pǡxv.!M)PF8pG~kO6|ٶk~(k0HHB۠ G 1 Q=@ fotoxx-15.11.1/data/images/anti_alias.jpg0000644000175000017500000005336312616075370016654 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-15.11.1/data/images/color_drawing.jpg0000644000175000017500000010444512616075370017377 0ustar micomicoJFIFExifMM*JR(iZ0230*0100Fotoxx:mashup|trim_rotate|resize|tonemap| http://ns.adobe.com/xap/1.0/ 8 660 728 2 2 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?mfUT7d$cW\yΡ|e;ʕzsU;0ojocRJ)>$Uo÷Rmr ?Zٞ\ xm$~֔#;} I9ؒI \c$UI&sl(l9Ǯyw6马S_Imn 6ILUƺXR:"VVpZH9gW-p?4\,Y7p~Y Z_-/[0U<~u/$Zϵ0>-#8ݻ?1Eis1LmmƼRK/VH]xy$1bjs#L}F͖X| ?CQqXFY=IwֵFBmֱF?y|W%q c'*agҀ;4xlһl1?hOWFz.+o7g[\zʓAQ`:k=-#[ 6;x㱷HaK*+AQ`w[C#QZiPIʥ]c@~Tq?*,1&ˋHm'"!=l5%oBǠ~TX.c}NYߔshk-5 Z~T3*)f*:X.rW[uu+ؙ#H: 9"Ub :pMKǠX.gyQQZzʎ=Ns?4z~Tq?*,3(G=Gs?4z~Tq?*,3(G=Gs?4z~Tq?*,3(=X.)50xKjRH6,UkoSU0'(HOuzZfMʸڸu-jM;XJwZTn4>uiGF֤NHw.DzxV{M I&X_,unP7*Lj:8m6Y-Q$1]_CKvt#{g#9KAV1 ›IFïzbm?OF}8&`Trjx5R5t+mm,"4I3ol2Gl"?*g==Yt&uYl"ar0OlS+·w:t7hdHA3A]%SӴ;I~j{i?1g@z0lX# mǩ5 7%u\ZQz3hJ^o8X @y!(֍֧~O?`/_JZ7Z9j7.z_,<oJ:뚬`hwHKȮqakY r)܎;9gI|i<=Td+V|ǴS,7ȆH!Iá>ڮoδFOEc7]? _]? _-ѹ:d>J>Jino΍oϴGϴZ[sxtQV?fkG+kG+:77@>JVOZ?Y lN{Rd@cc;rq\I{l |.daGC]Lw-.{E[O΂XpX䓌{=-2J}ՙq['͗O$4:BGo!RM]d 7K0c~-ςt:qQ_{3zQ[bYЦGV[]PjMlgY obM.|;&LF$0ʤ`dnߊO%jb$q= A 8ĈdwRO}+,bE v@%wzLUͦcWxw9\OZ}6KUռ %oU ?7 I%*H]w {։u'#)H$B7#Z`As8IB,qcEgK|^c#Aua-X`dя q3HB/q~LuM??I?LmaG4eE?ښw}TeeE?ښ}TeeE?ښw}TeeE?ښw}TeeE?ښw}TeeE?ښw}TeeE?ښw}TeeE?ښw}TeeE?ښy流z?ouI4bH]OB#"@(EQ+HX Էye yoŴ,2rW++Qݥm,N֗ ǁ#R jx0YdDP@<ϦhBgagr/a24eԭK]5h r{uM]XZXR3\^[xr͔q ~#5.Ki>ylkXDPA9w^[E{ KGXLuF2p?S\L^dž5 ^ݠC*nO9-շ?SeӬ'ܙ EwmŻ"C#B Il1Nt[ [NͥvI(ЂFxڝx|P_fމ"1 F y&1-$UO9.Kr lx/J҅Si #v$`GސH.*Uԥ(ıwH /!ڷ w?Q)I=ͣw+}]QW#vCvC"?~']QW#vCvC/CuOOr?j1L]j1L]_=?~']QW#vCvC/CuOOr?j1L]j1L]_=?~']QW#vCvC/CuOOr?j1L]j1L]_=?~']QW#vCvC/CuOOr?j1L]j1L]_=?~'Y+ay_4^3?ʟu2 c7vlSoO٩BP`07Oyiրc֯^-jLh(0q0; 0AdU{>mNɓJfy= JpηI?3CYeOs85zwks,cs,x$گtw 5 IMfy|-M IH68t]E<yQ$Ey}tC-E ay+ R; Bvʖ>5>ewsؑLʋy'*7}Ϋu$@Z<ǁ0{5:tfJ-5t98$MYZƲ &6,Ep3 Y6C4Ri7Oj6[򡧐9)s(=GWߕbOSěs~T{D.~TmoQCsND =hmoF~TߴJ'x~T{D.~TmoHg@7򣑇BoF~Ty=qJ&lJ9s[Qߕ\]wX9ͭoʞf~1%~TX9@^~Tg$ "}e9,Yw9B2s՝S|Qz*\|西RtѿRtނ:2(jDǔIј? # QSެ :ExQ}n Eo˔#'CFKIk@b1RtѿRt֐bEX.fԿ4?ᶴ|ν؁ m(Ud>Ib$:*cE d}jF#瀽tf1qSB@ sg4 O(nOR|{ F$4H䓟j]g?$/)PXgJ #9rE |C/)aژ_4n8Tز@A9Y kY'#YmbX: ԍt2MkK6hZc2\H$aW:5TFrp: j'ܛǭ8. }<7ZMxI{\w81#L dԜrhzRHn1zvN{PqRh{qю)qޥP=#ޕE;fϚǎ2zzT qM0 S2hh <)5 40BqzRq8|`6 ~TtiaR0D2=iOMaPT`\Ӕ;})fSTx@jAy;SqVs/OXIY!=Xҹ&-qZ\)җҀCBciS (MaSd3|Zs07sZ"2xXNsGCI'08BH++4[$N0=)r\PFGA@S9_ΐ (#Ҝy0p p(4)XwIM=Z GJqwQL%QUa <`㞕}63aǯN랴ari✤ `!$Rv3w cySG$wp1qJ6 YNދhPjh[[h#WI\$+F$UCpD%vxu=ܶ հF|ö:.è\I#K$s ;?*oGe쒽EHvtiA:Dz g0Ixit.]WHj`E\ ;z]èm,m>8sWX^(>s]&+͓kczSN;g2?t=r?#YZ;0qHQv敎N=+YHj^;TlU,ڐҔ#R昬!>ƀt ɠp$Qv֝@Ny%jr`8ڃϵMa4S@˖<TYG9=S+ҭ30QI?j'T`\{`uc(q@ iÌgGu`N~\tO'_`('I<7pNixӥ"9S -o!>FsN !JA5At3w<"8c֦[iG3.0n_kuk]S^=հ3,ד`rQ+)l\WS?:tV-,8wTz }3̀erI<=IyFLJ)짨d)%pG3c@ cvIm#5֩ud:!tdŬIEy-,PY^A#cZTbDPyڎ G,vF7;~hݼ~T<&W`㷿z|ۥRgm#&Ԛ+Xv#@W=Ag갋o~Qzn[ QY;_sTfa|u-(t B+mJD7N_I躀(m~-P߷(O#[ lXm%:$I,D?q5B?5p?z椴1 u8Ox >w(74g46 iws)~SA@ dRy4Oi\M8c=ɣ v49#ފL(i{EHpOZSv֣ə>s!pA'01LrF)Q2ѴBApXӮ,2ïOW1Eqak} ,.713^IC_TB қu* }1ֽVG5bYC4d6rW x?|S{zg y3\wW4KS>ϸWp5zLG{ 40Da] 8Edp15WF9#183]&vZ\zf~LuF!D4GN|%e5yǚʼnVa펣 maXюB J;ejr> 1h`ɼ~3;U4MuZj[.k$ ﷧9K ##Y.Lp.x,#5Oz!_1>QOvF*O[&& "n5 g \gӚ+qiy-ԉ!iYm&^K.Ty"ӎv>s7mndUW$#ץz&z]8g9+VRM:ݭջxK1$he[;Aۧu&}F ZqpnOX|64k{am+Heʪm*h<Z\i,J7v%+^;diZlmo&у-d%ο5{k-q\}=[N⑔EI(##Uq PCsOZ\qH@8jW͌i`'EF>G3Zr@!1ӆV GZRqޙ<֔*l (HЯ.R 41ŗ)7oZ&2*GAi]0- ,~DéۚisGǀI=[]gek[iK`|N> ,M`)7r }lX/Bkqy*O-AQYrSZSӍ1wRF&M<[myp셛z K[XmS{נ@H.,Ih$i&ZwƴuL B}Jbi8(pj>;zI>S7e?y \Jk EwXЌHG]AY{jAF+ZzZt0zJRl>K6]۷>Ԙ[y/<1mla^1Z3,+FLѯpʼnl#ьz DTtZ^X]f p7{t_Zjw7vv2]] +Hd1dz7)pQ $Ak !p>&:c?okx۳$PNP8+bnO X `k7I+U^2Mtq[v[jֳ4x3HNq_8d,;o,K3gSkGn{<Ǿ)fs隔S6zw[ɀrQ:|N 9$`,@>qTdI|i>mg3X̷֝M/]\nOV4c_Ik)?ЭgE'AWZ{KtmJwzjӤUVT!tmS톥j޲4.,6Tcw6`.8"# ic 7 ~?ҰfU8;9Vf.Qra=_knEü2\I{ [O;-akāv;dcLcxP6^·vlX$=$x X BOu:ebeb^qr=1SG@tSRR*({w6o0]'Rj]<#JdÜ,@?ʸCPy2|@w;"!n2c3 np\>h5 UؖqGm#yRph]ȍg rq9Ǟ'7VY-̍pc@q sTǞ+sνySt$O2=R֛J|R|nr WHIu0N!x'㺂U`oI:lR^Ȓm9999]âuDDW tϽa)5.d]<\X걬ɂb!WϽj@6^dX83U%tU#Ŀ6Ux+«3Z+{Doua9!HnZ)F荙x>#g!xn #Qk+ɣD%#a67(kHgF}k׼CA順hZ9nOQqzXW=++9'7&pXnfC]ExGGqW7z}]ɘ9QʁysC,z"J'zZBڌ@Ɛ3E8/U\So,4#6r*ՅwlozDu@7qč޲DK2=ϭID፬C#+Ճ9W1IcSEUh͉01{ܚwN0zWO};Rk=eiNF5ظjj_\C-'kYa[nHV#9ֲ\^-i3,w?[5MCO uuq}yѭH7n22q߽O8}2Ke?.x }({/úݏF5KX16FzJK?d{Mby(|szpi{MV 3׊Km" ]^69O\mYTڨe7 30$}=ksqy-b |N;GOư–W%BğCN4]Z\2+y2ĻݥePy=?JW-Vzea}43M (d s^}AK3@by%(c> 26+5u+(~}+Ie9YuU YSmr}>sjljhf#'ڻk=ž*}G afHue b2m8lx[\F>1H/c`m}QMGj=jĥ?J(4E'QaWH2 k3p9'gGw,A8$Py|2N+Wē[LJ$lrMVѮkX'16BA.r>?2M*IRNF3Zƍ kc"RX20ھg-v!!w\̳;(1TYR:>Yݢ|C7 r27~5o..s]4ϒlXu߷W^\x=i3OhX]Kqij HxVrs+&ϦxCNI G2ˌcCN:^xv m*ϥsjz6u;H$V\Idʤ#dOzS,$EO>ST rkEL OpHO x魴=5,q_`~,-u8Vg"%b>']K{^{=;z7@+JL8Քї4_kGQ x*],:"L=ZU<)(\⹴ki" &WqY ć8HIunQHH I6OӊY\.}Ď;Ӈ5|90ǭ"-2Wwڶ籢0QT#BF /<Xd{u{}N#$ {pn o'5 L5șJ {(CHZE>ZnBmྔ"]-5u@H$mǐEz\)!ԞK C&`p#Hr;;WOFJ&KJ1 Uvk*7Z2Y[ +ym;b>U>5ZkV0@"j ʒ H:M"5]Nya.zg=ke_IԜ$+0kVr",vwS,l24. 4HF==u2O>2#S[&MoO;_scڳj:=k r夷 (<`rxoUmRX!_ai/jϦj@CØ浑Gٯf=+H#rIWm^I(,%vlT-Q8QWw %5kuHI-]ya'DI={1[١n҅?õzVfo4]>8 ې( J:g7Փ$z6]}еqAN j}oyGsFr1Nթk{(aY&KЃq{WVזO'dHx-z5CX]izslCvNUl8>udʠeV^U0g?Q^ZnviJ6+gdzR %doA_VBdɤ-> ?nH#ɭ[\$\oDH Sr9X 6[Yi;_dxBr+KkkXwOFJ h6w7up~YIlPOz56k}N/Z&Y=07`O~keVxz Ucߨ2Q1b5OyU\l.MrP5Ii;xm:ي9YPIp~O hwPPLZ0Pxx} 7^ʴ{DsGz<++KkI !cgw@cg'dJ=w{-BN3O9v,ּ~]ǥA%"GnA57X񎳦]>lK TWxZzêj'PIr# BW +:Msu*KC]>Sc>$27$=3]w6K<颒s濘Z2>Rۉ`3U<jxs@YDo[z$d]j^%MGpG*l $ c_sқ4Nߊ}"0_[ I c`gqqi6{O d OM j hZXO([BnTu%OF,V,rIi2 9M943{\G09Öe-[*%F 9>湯^i^% nP1; u KC;;Hzúfobxc D#?Z&^D5y>ꋅ$W[xm_.fx׆&5j[_k)P6z}9߈ьr XB ;Fw jֵ9Fe̥Qzfkյma/V$*##y$`ކ-GE75v𥾖"YE%K; 7mC_ҭcUi`GO^$/?EGi ETBi/4hbNkwCF8z˛&rݴ7O6̷qdgpWx,|s]O`r~ǡi]˴{ ꋀ0x⎛o&ue%X ]JM3]::υuL7yl8y֠OuȈ[. ~?E>ay,9S0/"x<sqN6Lxj=uc!1Efi]F;ʖFŶ ??ԣf2Cen:};U wK,s-8lt#8TzUhcX 𿆼G4Z6m\jpMa^K{.#^dO*=ϵX`.v6uzw;Uam$B Dr@V\x׿^Kgy#,Rr*9oҼIG:$3rkz*ətߦh`."u[-{Ԟܮ@wm@fcj7 VX r=kOvַQ۳(Ĝ֒Y+s|$R_ybQ3nѢ9o@#6,eui}2izˉp*@={׍hG7 a&9 w9_Ҧ+hI/$g]r)P$cp6|߻Z< Mg\Fdި$tnc^ Rgui|CKQHwg BZ5dvE7>f<׏^j)aQ*@@}yωn5wP 7.J1PQ֢]TAa6ׯmm<ؚ_-PՈȆ}e!"E OUzI{-Uw%w=+XEJݙεn1v.45s̢ԧ_iaf&BC>}}h [{Y.a 1~q׬jш x=1ֱN5мȳ%) 0USؙQՙfVt"'ǘ뻒I9?Nz_!g6mYԮsΪ:MԂ+Yp2KHqi4ۛdspnw\p= z33WVQN#t[ۗݞqMl#K "YBz&} lݭ/5QIT-#4WsFEG?=J[u ~\մ6"ITSf3qsKm} [8٤]ۈg\M6k̬c, );z}+#CJA YU1Wed'ٳuk}F- K8.&Ѥ126q{VuO.F'2+¼{/ѼPW?k5 -ؙ<(謻ч g:qROc:M0Cl&ʣ7v_m)y$^pI0΋mOZF>!y#7M˭ؾ&[v&k+XSsA6%ԫFQO~^:WA}1FVIxۿO8st] yʣ]3۾fe}2kl1[#rI2kJ\Ȉ.gcǛCNҮXT$[BNݱ@04@S*$B+ѭJ+O=3*smIAS&W;VK~6&3ŨMWz⟲$ʪ?x wJKGm V$zׅ5v #!Wר^UHVsz{~AL;M+k)fJ!ac/'qE:|{}]ڨ w=3Qv\i햐19U#ӊOkE׵YKJ]&^}:x?.lH<9䘮Xk)M`fY ZNm!b9=1qD7v]h/R9V|o/]*79] ߇bvk?4sPɨrl-iGnWe.4h1\zpNuuea`cH젲 \NYraGIrUp=Z ex4GFPbY9)z[XMë%Wn8kJytJ9dC"s >cD߼>*>,L?aچeT%`g4t@fO#[&Bq\ Hկ^ᣕ.3T7PqRbTk@ x0vyϮj֛qh! }2=hM[?6P\;HZX؆3E:Rx } zQEcs+\kwikjk]@. \WBO7:|3]L2}܂c{פkz't8Pq\χmk4WSejd `fO{N7L1<$Q 3+RԬ嶷}%8зȌʁw^k[}3ÒCx4 6K:;[}U7;A*9XӫRDƷghU[CN洼~_vr$9 k׮//Rٴr>TG9t.ϠksF𯑔%C#*)^ 1;{ iUKt]TSuC)IOϷ_J9K#Qɑd%GUWf0o2 ڳwqѣ"q,7wl`ˌ T3X@<\s}W'm79 wxTc$'Ƿ1WUݶ*\]zkIt~ɪ&2rFcqMIpcEgD)ǕsxP(u"UK;q?F^-ʎn  ں#z{Ջ$9#@!QjOC8dk~F۳ ε͕Cpm"gG.r$*2w)Ep}¿I[ ]DΝS;ps^ku+ĚvuPaϨ#_Rפʆm9żd䣺ɑۓ^ jHeF}ui :JikA989$s!ی2?{(.w Hr=N.bq,P}ko-ݨɥ g^1oִkGrSFu{+\(#FC OjBu[Me,x!~5ymU8|(3OZWt]D)աi@FIuVOr7:^8{@Q>7e}y"co+ ޶-|ge42I<}=xvWD$#xIDl睡r=i9)]-1IASFb_3z1/ʗʕn"C8l!HZ#Y;Is4溞gOh2U4ێFqY֞ [GJRB|ƖBvz/we p"@<:#aaw={ Zξþ*=(E%Tŧx=NX,p",)f:We(\{+ =+u}JnŴ7dL7g?n)l3__K4z}P1P9ӎk7߽ݴ3]DZ x`3kfGK4_9?1' }^o+}+ZݛN&To/ QU=>TюJڡ-0ůC7B;[:|LȓNr+wp[1!H<[>#2?W_-.YX BVNn||+ǃ<[4;Ρ11ԯ닍FwbQ[r˹7=iZYuH|o{᛹$h~rJZI$J&1l!!*u57/#7m9 qX]`*cO։maob=Jx d` gn| ۆY}a''uk@eMbT >եI' 2QmSIXy:l0cƹYΎ~|8ш qQ8u)o%i1:ekWe3KlNXKH!qTN1M35bMjMil_A-ƶDc풤D`5<  cu'?ʺ6|bz1h/. bItjkʽ`?{sڰ-of25.r緭h^ݖؕ59<QM\[q5,4d0W 3Nx!8+R5AE&x\Gqk퉜szEdMݣ9 `l-&_IgU9ΥѮmDu ad'>xH@IeQςREkR|mwc6'dk62#\~AZwy*/mFC s^l B]]Cud[p>>ҬEQym^kmNNz^Setcẖ*8SZm׫5ܘYP >ִ2GP<$Q 8|W=fK)!q`G^zԾ}[̊u5J?J)!\ey=81.M6{mj+JX0񯟵@clդ>@]}ŝ+Q-ٽŷш- tb|mu-P/IN2Mzb_إ5d*C;~^!XW`y_e_m!H"*3p7cFI <#?\҄]6V:Aއos[5wF+.x `.0p0{ʰF/PyyV#,fj/6t.t t鴜p01MO#SE]5ٰ8nf;~>csEtv9P" 6Ekw؝/uP7y w?ZaamV8 o.9Vbۤ0INuϪYG٭ڤeUy?7A>$mn]3H7-Wj +sֹ)7K;w5iN̗,4tkw(Bln$@x'>Lh&HS-s7[(m>e `gLf /V>bi1:~xm9'' ~ΪISp4\ױI7ۢg1~"ђ|nj^6w1 15wu, cQkZ"62`9*hi.Dh?7 d]x#:hVv-9T q WI"š5֛lFƸOpnN{k<=}Ds+[+nyvU#맘uw-V!Rr)Ry>a=ӹ7xHpM\"d o ٛn5)E(юu}NGc9c ~nyC*ۙHLl~ |C5<~]1&>_o#FWf5w$$xC`W+S꺝j+pJ.S`%q5ZhVi)9D;Ycֵ|=uwi n0@zҶnjH#],zהem(߸$8͜S _ =.,&0@1YZsn.":T2l<#.y3ZMYxQC-\<[+u4 mx?Wv\zW妻Rv}w|#])F~.U"p,BdkQ椶&ly16us1nNKUm#CLsJ;OZb"fV4 8VinK+n?:>w56nc7}Oz>'f4Em՝ʁ;Z R].o K{ muk; (f;LXi\ 5 QOG Fk9D]9icv |.ӓ{V=隿-"FdfTIWp~5O:7sO*^V998W8,6 ѱڄ]QOu!?K~⻧h9~`NIʪ<j G~4$R!$X!; qO$)ԧ #p1EcQUajvĻ!;5r0~WJ6,mHf2X1@kjV9u8cT`p5[;IF^9 $qҲ[EjнJ]J_b$Č $D.s?ۮD6gڣx{RE`ABqٻU}1fG`K|7Ե퍷D6N0*g>dQl{&-q5:E^!IjqAo>_`Ϟ770t7 'y&hqf8ޭE=M&F@r2+ּs$+[.ѐA$$QF^MVɼ>Cq$fY /r(qr/'Omop *Ӆb8zIC:xlx&X4:;2;X2@/.c-W!)yK,V!bp>RIOAχXBeWwr{~UPE״&7" #<Ϳ@-m018C,I?B?*Ӷaj>Ha$QB_PŸ|~'\EuedžEV͕W{Wi\k6rjgQYՆN9uŮͥ>629>z3+a$۹Ɵ 3y219\nU< lė5a6VSȪq>Lȼ&Isbpg3Ԙ<9.Ek?vbI2UY;N;gkZQxC"+쌫2 gH~xZ'ka(e#=3#Bp'x_8i);6F\xo Kkmp?łsRAψR [cE ߔAlzx7Y4eDLd/Tkf#s=s[ޢiRIV^,wk{%21iqOxE) ZpP(CTIkA(nJe8J[C ^ٰfڹߴq~h,XJ .HR_"(f2 GsX6,s:YXC ߒ=-.-ߓk]]8f(F ={Iq5Ѿ2)"7RdP䑴r hfѽYAP V@uBp9n<:Ă+ gr"|rN0hCJ!ff^d+j6 #ڹ;QI,;|\0#9\QEm or$Xץѡ'͚ [1pbH$Efͫ}-$y2p3ފ*#ռVi.8I;"*)"oukiʄxLQ|:5DKE rD:zZ(-P5褸Gm',I7OGkOx OqKZ犏bi,ZX42i #V 덧uv[I>amHHQJFՄ~lV1Ȩ7i<9lѣ6IVȣ* (4q-ͅĞ'7Zͽ!\ez1)ZO>4#i")Mhpao[\4Qc/;cG\hm3$?IErfZoll^=ILȠ`?dxx~3l SF` ~ڊ+Vݔ g\ڷ웆NpHNhcfotoxx-15.11.1/data/images/mashup1.jpg0000644000175000017500000002754512616075370016131 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|sharpen| 4http://ns.adobe.com/xap/1.0/ 197 263 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?(98UVG۸pC+)NX'j溽is y $s~Udva6ǥ:JЂ>bܤEq gK)gh ֤B\[l$̇逇ciN|R1OM.8 gde8$늩$P'sǸϵSMGd[,,;^1Vfl A$c :"c3Iejwy+%% PI" vϣ%ܞvڄcl}-WvtҒm7O ID +yk 㠤Ǝs*ң } B,V{ 8xny#a¡=Gm4оK `tO >`Xڈgb!]԰ oV+lqagK$Q.̛tƠ5k3Ő]G5i~K݅ami20N鞤s]ttA"&L`X*[ PQǖNJtҀ8cźj-1{u_Aִ.5f=_^F7en݁|p;r¶ڍrK85DZĤ*cpRq m,04GQ1WT{q.s']N,#Y BY1ӥ_>cT<3n$؄?tqɮ-N݅O>oƋk;K5e67bT:gmj0K- ]kfgFn??:{1kS䣆VI<}~-8u %3D`qz# f"}g:bcMC J^:t!~ Z&=,]FQ>V%ȮtRO[Y&.qϯ=qS9oyhd T"9WEiceVV֡XC &Rѓt/N$쭆 ev W/i]xTe.T;I cx/Pt+AW{[!ɖ6y$WIz~0͓NiI P[$v5m} azaKw[$H Kc$d{Ep>,dM[)Vus4!wB@@c렿Rj/k 5- N>Q9GW%%h4]^ A4 A]/-o㽕gK5Sf1WZ!A{RןXj,,}Ejdg\ս#Ěnx/@ߘ=~|mEy^0E v1\$Ƭ1pN}GxVפ~°."*eg}i&=1𾡨j{,R.),X݀9W5d_xd[5 (c>r:`٣aAԵ:ƥ]+dy, 7}+.gѬ5XH"pPA ׭arQHaEPEPEPEPL!xfA$r)WSЃ֟Q* JڣnY[֫kjcRI$嘞慇Su3mq c84%3#nCdBsQj#NwHuTVcʲȣ A`="E{6Ec =F׶o| &da=&i.fDȩ;G!s*n>ϳ>NL~Z'6 kt)Z3b\tKXKy.bIF7w0 D/FANeWBF6c ^칅D{dy@zjs lGNB% AFzvUwh'>o~y>^yҗh?lw:ڀ%Dv۝Azޕc+kv _OQ;SWCx$=@!]3KSȁd.8N@lX-iYG^8*3GE[ Ϸ,\( "L %k&Ldr:1pđ&s((C8oQQEemm`-BKdH xT OVǶRԄH\NɟY}.K!{hn٥W{֕@.#W.o(q sKii2o ۛh q}Oh^`?R 툝͌vΕodVWOom1Q\zK|$"YRF ݎ\:P\^[Z< q:D q$(?NvRH'TTA ֌FWO֦WnV}Õ\8`Yv:Y;6ɖ6ϖ} M\) 1I=21Tk{EQf3q{O:sP"rEsM$rwh"+цUQ^֏w{:Ax$ 8Mb.!I#@%·/*@~W.kÚq˩\$({L3FBF`H7GA%66QYac#Cs6ڱsɤSk|77 }+#.G#1q1.vW #ru3٣ #۷ԗOO5VV`bLB㝸sKPz}GsqMq z(5b!_65z?gNS1~ƽ<4,sp * k[Ɲmco!PvCq:ؿATigv1I4:ZK3^gy_9N ۿbQ\6m7z5mn#D yFCw'ڀ (Š(n_/}P ,Wd~UT@UzItT쒻I8AaemXCacm=Ű3j\GF_cGsxt̿GF_ u%&tĊ æ@?09l-<2:gm}q} K+;1 v'h}h,ǫΙtT$oΙtTq,`f_/}P0 GFꌿG@㏭ $t$Sr:77̿GF_cIMtTe?uܿGF_QMtTP((((())c Z=&p*ru"+=;[$/u=!O7u{BuF(̍X_:tIvrMή9;>uE?Gu=!O9y[.] tMAO8==*տ庹1Εd̲޴ !7GԎ@hgȺ'h.?~$equqb;_İ$ņqpjX|dnމ(`I#ģ[ۑH̉\7AB?:ɿy~QavV:ֵi'jtbyԼ~Z&HI~)E?Yڦ'${hEBp>ZZt5]WSSmB-lmFM 6Ə"zC|]|hԞ{{ oxG'p?Z4;)hG~ h # uA]H4y7_߄`5_MKƖzG=;yŔ M2:8PN:zVӶmv#gmG!4n̲E,h=hUWQfX^N.%>cAʑk6}#Əb*7XGps _9#>bkb),ѤN!lghfϤҍ>NEY4r#0>4T:ڊC?fotoxx-15.11.1/data/images/mashup6.jpg0000644000175000017500000005037412616075370016132 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|sharpen| 4http://ns.adobe.com/xap/1.0/ 317 454 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?)'jjspuiҘ`I=N <"3\cߚogdv6H*{L͂)p?= W}=_~`U?ji՗I][GZS`B+įu$N>ٯ?WIߦeߡ%?stK&#iP)̙۴dq@y~BxЋpu?-|bUs0> OIutE$ڹqΩgٕY֝WOus]j6Q W%1?{z_-JgV6/z"PBI r@;*bI qH$2pA4-2V n; P@' Փ-?,c,vP,q7'1fhbOLo~To~UK{ TyvZus$+rGAҨ&Gh}y#:+ȫj CMwp M^-2QiĐ6HɐqXvjimҦܖi²Rq^:7H,U-on.`rdeY`{kֺNpBB"3cq\txm"%ΐ!]O*m7΁qXir0FuFhjI:ӵk돷WFu+ݗ BJ]ѽ:bڝؾ5ir]+y_irQ_qVrj7v&5S<=%3Oz2"G$"*F `d8/kZrpsoj'-% Ĺp8dQ>! .]U. 4F -&Ry$ך:1;;Aon^b,rѶgo4gPӡ=ٰ%$0Gs z/ƝdC{'Hyrm`vwێIF u;rms&1hՕaqЌV5%jDVQ8>Re6x;sZ]+hpk ֧r[jhqC ҠۆEU_k/a=֨pS-;HPOC?St5E kp#up^>,Zռ5!PD;jv'ZtYowC};5!0dg#8 hb1 Hz(!f#!(C ( ( ( +Y&/#B&u a2r_Z :l_Iҟ3k" U=zv]luWo}nyܟ؃R!ktr;֟5KS{UX9aا-JcCy\;/G5y8L*YFOuEbƗ5O9Y||[>J].3MW!+K[,n$c5f_qEqC(e;\R ٧a\ W3$g5P4ג%D!*z'We{ͿO P\z? rHZBq$?VdDe[yF!~^L)ۦ0ma9ϥsΤta(^Wki2M|GVvo,k"jLzcXCEYy N~Jlnj7(7~Q:s$Ejt6Wk\j|}ȿ_KAK/"8?ST:)"}pA9ڷ9")`25K}Ƚܪ{xjvJ[`kk0Z?Sϗ_E`_EKl:dv]l[+g$zg\W){6)a;Wؙ6;@śtn"y6 7 u#Uf?q|CzERQ\ugLE5x2ssۚk\j>%Ϩ ,4n$(7u+oXӯktAn°ݒGNJNad3ij~T=3#$}xySƥqhisw囅XhԪG+qti['gT/"KAK/"J^T'- ~8穬Vo<7/yo03 _ ]}_ ]{b(-̠R^R_ ]r13X%k-8og@_`_E"A?ڗ}Hn1܈#$lspHϵ:1S"( 59$2?Z3D8>O<]ڍfa[~`b P֩$V qT{/jzC ( ( ( (#.$G2 `4մEU[XT2(7jj(Q0xP *= Ym9 mhmΆ5*F9>'?v8ORQ@zb=0$~_*J|qZg x( c*J(4=\'Sl`l_A).Q.`=H h Z:BfhIǔ6xj_;-͂R?onaG{xu ;k sjh0ʒm =5 ՔG N6ơF}ǥ?{xtooΈӌ>)՝Ok~iZ7FC д^Шv>ѽ:?-?mobqO~?EQ:7@[%]Y'ԔQHaEPEPEPEP-_U4F9V1crIUx@n/mԾ Ur0q *F8B6ygꖍ)P[i]zm`sg,^bŽ'R7\*Rx9DLKtQ_Z_swgAn-і( 䜁ۊۉy$4E̟}@Fy/{}vL1q2M4D䀠~[BZ%mƀ(tmuI)[`څ0\Q$p)6 mܘz`uiw7am%d%RG\VeYIyVJ4`{dRu)uدou(+\ E (\mR3'=s@Qe}I KFɏߟZڮ_@i\\Y4:}F Sћwo,s4PB+nG/3ֻ$Wj`·qT$I +rOj1KnwF! $F@@؃LŠ(i oW"aY:v2IzQ42kA?3RYOooao"E;-2[7hኜuWNG3xWsEok$;e1R똾ЮteG5OxBYФ8FCcx>fNj4}B&/#QrOj_06[ܠ4r_/9nN[gIt"Fr 5cOޡ͡ ͣ Y:f7v:b0ph?֊EQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@S$82Ȩ d$Av#IEQEQEQE4Hh ##EuEPEPEPEĚ)Pr#$V:"Kte9XRH%NpGQEQEQEQEQEQLD6WTE,~4rȠ(((((({@~FcҴ(iz]Ț\uewV88֪ͨ\p,R0j6- ܘPG|פosFwⶖ8K_kSx|^V)(.v˿$+sj7~u=Ie F#m9㯧n49'4PGg.𱺎h"IVC A?uA8gnnS^KѣH!eb^2o3e]VH59X&D/ V x=1s^OcxŭjK-sDB8(=`}ꬾ%eҙ˫ Y[m"gr[8p}3^?U{$ՠ9' @pڧ54 ^P}4M@F_:MloGC{8{+xnEDip'94{[+G#"2ݙV@QHa\ow HkA4WOs)M~uY9Qdv)6l]gP[H=:Κ;D!\cyzףon~c/4yfoLڔ| k4K`r990sV?P]o \};j 4oo/QEQEQEv63@؟@3S "%Y'cx] @i((((+^Q+H9vFu}e'Ώ'μbq*6'i'|/= Ak!fv;Qy~U;WCU!@?ZcO:pܫ֍.K_:O(%~25IM2{p]ۏ ?:.nl6hbp; FY[?k>_nsTS 2\&(i's[xϭ@-?n1HG ?5[/ 7GMחg-"qM\dhf>Dꠁ[.N?/M2 SA[G9 eW^ "[TuH.`X@J4roSgݫ^#մ-ﭵ .z^KP6pd .]ހ$) (((()3¨>UM695)YbTOpU~tϭE}{{y Kp+M{^ڴ3ﵙ7FT}M<8GG_t}l-Me;M[]JP<#%A?A\MjfCuj!zO;k`:_ϭc6X?n1L~OO x_ucP6sݘ'򔷕oW{o-6@b]S̗OeEX ~eo˟{7W~Sȴiˏvs6ps@k>_n$׶љn-"1,`VoܿnNm> aY(!ӵ%tUX혭Zĭ!#n֝KNmtVծ6ة-:('EZykownkVI һ,3;h]w qN./ AՎSW}Ϋ>Sk$/QH_'x__޿?kxwIӯ%5Eh[>թH_?-խ,)ŻO6aTU"קH5fێfWv:ޜ('Ђ@4!ChٷTLv'jI $E~W{E3I-4`$s$]cݘ\c}Ls}LslG񬏴iQ{P(c}Ls}Lsiجo}LsMmtVH#, rX}ɦ{UV#3; ?aTӦKbP[@(C ( ( ( ( 3-TP:c%Ikp22;qȪxWNKm6I/^ٜ1V<.?\h s2O}ehb#'h^d+c y g3Lƕ p8!BrU?(Teh>m>4\,^2a??ʪ}}h-E|sIko@X|B~}}h-E|jsH'mPQ[Ds {?ehrC m!HB@ ~RHeKVK{$$c{t DʁImW]jikoٯicM;ST] Qca>-9lT;ajt9y`sV\-Yn1}=]cO4e}Nz=3HfKt{ E`.R8Lg# PK--+ۣ4O+GxQ$mfxS[ҴGr?'+ 8Ɵln is2c=}}wv\C2GKy2=3K%Kcc59OP dd鹀"OW#ږk>Wm{83St<6ZY뵞B3dvP-9=`>i>dCQ\[^ռsY ȚO}wl.Xl-0JmN~s6?S4>-&nT^H,HUC ( ( ( ( ( ( ( ( ( ( ( ( ( (ج"]3PG 9-f2DbX$&&ábZ/1;#$uJ&ݑ? 1}Ny-Otя _7Y{z_z?XA:o/Snl~Wя _7[l'ԦocePc4jm2*eغps6:XXI.`,'Hו:x/? |+8 y +k Ͼ~euhZNvme?,"|c : 3a+J][tKn?':<7$6мO c-,j]@ݵi.n[sQT}o?*HG7M7RnS9P^]Onwb%}ޝzSlo P7JFU0}r*_M}GQ)ΟCT-F8X钬Qno\ 䃟ozHYOئN[ϔUsu{-.KFd`4mU 1b{7tw*zrϜUFIGH@, F;p:Ka+%q΁q/NY{'irFvf+I⻁EpfD@Ig\7B9LT?ko>sQT6)ݯ?M}GSRE@ Ua`x'+Y9',j;h_:E@Rw'S>7G|H?θ_B '%^ծ.2*Q .0o\.PA?z?*?o?* (g(ȸy}vWok/jRfM2P1K$>(0((((((ue4% yb>'y&O~uM"[Kv䴂XOCommg+l? sn1B_ܛ;xn#6| x8#Cg -,Wln6'X$9gG"QNƇwWqH??????)\@>?jŕ+EcUTm82pytUs-u~xKF\b6?#Q,j%a,$0棻%3ԯ4 a[`2px$x)wڏZ+t}QOo./}}qqi&B$h@T Q](ٵV6>i:WM#/j?imG?k6}gvU}Ik=O{e!DlN B`AQNN?տ_j?i: T.lK .MFFkEU; {~ͨ7Hs p\N%" `yj^Xf'Pq[}ޘ}1] K?տ[RpBc |I 8=J_j?iL_u>m:JFRm<®IiQOo.j?i#%kInnmSafvoybAǽwmG?EQe?]hb?΀)}QOo.j?iw߈yǃn?mrH#yb0 FH% :q@ E~iԏo.ڏZ+uv1;XuSh\\*Fq6?ώy.m$,w~TܫY}E(uv?^ZI4qlֲfV(}2?t=?' cS%6= 9}QOo.բN,}Gߎp\pźqj-~u P E ;Oj?imG?I Z]rnn+4B cSjCڏZ+tCo.1L~OO Q?m'ٵV[M5]̊J@}<Ʋm}v[wPFkF PoOm?Tr3)TrhE :mm<`BQ[I$$'9'b,F-"@0Q,JǀJU[zPmB[ۙK;10F n- :Fo ^HIi3܎C=SW܇Z1o ^t??Lh|C֠ſ![rhםOQC=SW܇Z1o ZXl'yK\(Xm88b F-amFhU KAH3,u0ʈ@q2;tkO6 HR`E:K,crJF@=IO+4Nr[Vb HDXklb@(o:Ъ?ыC|-;k·z)M'S-b @jB:Ъ?ыC|-Yiz?)hv/S!Uo܇ZzCa췩 cMܲ%G^ۏI{q Egu*$Mſ!Z4ZڪP?!E'_ΊCFh њ(4f(3Fh њ(4f(3Fh њ(4f(3Fh њ(4f(FWfU%%af!@}QWT4)4?b2$2Os/Xj:֒-VfVoaTzs.TysR2|WmkXyslr9-q?iGv4Chy9*X oڗy\;:Q 7RHY 8pjY,va]"l*̓|Pڗy\;j_qJŚb3XA{qa4 . #88c88,bwwG(irF03sWGړʀug#CO񆑨I~#2f*UҴZVϧsѶp}ATF0Hˍ->Uk^_S,C$ n9#HO!>Կ(R+V*K>ުb\b0;R'l5kV7I³}~rڗy\;(6A*I4coDiQt @sE5 hE!(((((((((((((((=vblYxK}`}hBu#UhUAI/;Tx>h'P).g}Et_e+Gn?1z&yaɨJiKxy<[%dJZ(FI%KԚ"0qF\eEsW`J58&xmdg|؞`=ixK}`}ie+Hbkg-ڠt[Yo4]^p9ǭ:_pH9LIp8n_ߕ>q?~WiwYo`֬dl9|g̎O ZtӴ/qt˽yXс#LUϲߕ>)#XO@X=wk7Y\i2 `ZʪjG 7rD`2rqC0&ƪ/%ֵ(丸6Q}‡1% gu7Wm-]JDP#cX+-Z>q?~Wi[0/|"B,jA(Xwr+[Iӆo;2>I'՟ߕ>6w$zP@QD_E8Hgfotoxx-15.11.1/data/images/magnify.jpg0000644000175000017500000010526512616075370016201 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:trim_rotate|resize| 5http://ns.adobe.com/xap/1.0/ 890 1155 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;]"" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ޝVXj[#0ns+uW:eSSDvY\cޔ,`뵅^ m@Ò6cҝĊd'V{|n9#%MAu2CT3V!zf$aޥa4tv]Kg ;Mϗ?)85z1 BF6A5}.<{XW:Ļ nE46 A#6x@-/ڲH 56Hj )6簦\]޳Gs)ÙݐjoB ?ĸꑵY!,qxz # V'pײ)ldEeN}}KipX1hh@d.˻o22W̷֬?Ixgg=hGȠ>:ֆ` cZ3udjʢ<~0kHg-aN<9T)/#\m.<E"!ȠS$QECw%bN*>cP%IÏʚo!}sT6C60ztdGJ$nʮJcJk)=M"fsX"?JBrUJi3@$9=F68!"1'*qNSڣf>&I&H4s敁8?ߡdlpi[.*F?(< I9H 8 *fݝďJkucٝYHF*fPI卝)A[rƺyķqrΊ,|`5̈́yvoaTJ kdbbT-v5J' Z-P2bRMS$ʹٚSku-g+ҷs[J77Jy[]ҵɌL홯M;Fi[JVfEs,9[zM3yoZ`ynB 3`?viz+k8+^r2H~WfH޹^ٮ%@Lܔ84ɠG++{i/h)Rh,yW%9Bk1>Y $1Å݃J%V)<0@yޜ' @Zl՚56oo)wdPseR-{Kbjlwe#?zMd%÷1D޹E>[vy4x ]L0rHTK#`e33y2}:.ԏG8%zh,;+7S з'#ӽ_9YZTHe)󧯠9W,Aǎ:T2G~fA 8n6jr:W9KY=S@aX ]Q BqRT54R܀2KV0yU•ԻF"0T[Dx.k:\ ZBj6Ҹ*Ee] q_\Buv`*.OQ!3*|;۟Ʀa5͍NL7?Rv rܨ^1[ /V$Y#zޫ\F,P!b9?BV$EI'B}j,vFM \e= c ru9q8sz6ަK).u8]XI*@SH,w*Yhc [!===金Lvw9b:r)ĐdPa1J&E$+;dFsx+I@52z6cba^O~+-$+'^ӡ[Gcr~Ι9nz \5ʖ20Ԡ)ITտEh0Pj#QF*eۜ h>?SE\[,OTgw۝c9k_s4}/~0:|D<<|KI$yP.ֺEh,C؞B.:eqHֹ5QT HV٢h4_M+hW6<0p7S\[9~، 4_Mff- x"6U,>9#mEhM\e=՝#[f[ƾ(a c,R[H$%*HfA_J Aak"W5̗%̿q?i4&7vNbn v䥌VhO޹g\e(CHcq[kPs5-]N._ZNmbWdw'?9^٨,QάI."6fܮZ"6]ޑ55qTvzRcvu$sUK(W??1B:~皕@N>YJ,Qs~K\Dg??U?F,sI;Gbh"HbI{81WY̠CS\i ϖ~d?*% WYMrHXV|usRjS\DXI+ϷQ]w ul"TI4iU,sSI-{.UA~R?Z%d}Zpα%(~xq^PI2r\)y%1p=XdI4iT s?I@DaQ d4lmیG?I$l818s>%Z%=Ǩsދ mQqZMcc9ϸ8I웄~`_ތr15n-M簽0k{PU/ #[8vu➺fHo0hq3ēX8UfsRD'AD28;zRђ4U|&3zT-kzeqiyLJAzg'fX,];QKurTݓSmtKGL`9;ը\(FҁVg'-Ҵ7?/EejSB-XHԐ8I+vj*}QQSq nؐQ`9}k\؛SWȑ,]q9hhH 5vM=jZ89IIQʕ犎GRT`SHsӥE H$~!Ū>cά 'p%qƦ\40 W\FV#"mVyuc\jVG8aGYufӽֱ;Y-?56B":~v>H^ZL1@Ɗ@o(;vɀxOa}% #?0 g~=Ʉ4 xijJ՜F$`nI>YxPY.$ *lՂ98] U6p)G>Uxd2J`ʷV޹k$e.zV"Zqf[}|  c5"p{I*Yj1 8HXCjtd<`[vڭƁ2X.n~YGs9(Z׵rڬ u& 6rƦ1SZM ,eZZgy[i<֪ɫ$O; :R}HKRRlgӞ쀑܌bKXFVG0;\}wqVNA{[jM&/9wcacvM CjRB8#AH(|(h+ة+&s֌=1ڀPNqZ Wp'85 ☙X+i|=8sH9RQa# 1ޕ Ǔc532FH`GֽD$V%t+fWuvsN_Kh?,RLsXp:UͲY%쓢$Jd r28zq}F+7lQE]Azg*i`/2艐\ӯ!x"K `eqՔU(ǥ]! pE HC[L'7m9xe/).Fp~b>o$\<Ӵ( )qsO|}*L]4RAcCvΔҡtTs*B*.>\\1n{b]n/8zQ8.HXߌ񊧥Uw.ĎLe57 `7mny@㵁M%ҝ4A5̒DG%ћ헱0K]e]s^;UHO|t-W,j+JZ#0GJΦHǐ9C)pjB栒s5vAٳР390/B='NP"FH BTN}+Tb1cDpܵC!#`Вy.79\׈6TH\9_Z8՛}5nnЩ`1µbUmVmmn)'ҦIG =knDr>WQEMuE'J+ogcKn;> bB<181=kM>^+K[_23@#ӵgZz$V6C**NOUI?JqzOO:RI9O踦BvA8⥇||gbN#`HdrF}($z bh$uCXuY@aS7QLe#&Z*&Lczq|SBhvz F=qM#~L~bH,8`8wF$&Ow|\߱0](&>"71I~(L~ߦ uoRUL7nxQ41I~(L~ߦ +j¦;ydft2pϮ3€-!ԲVF52)Eͨʒ0.sg%fx#l֝gk ވO`GJѻGN1צ+$g9k0$.qv:g,x> qnӆ=Uxn⻔q63n"6y?r7V7X*)~iUգ~U54qNۚ7CWdh_ޝD]\f-bOoJ2NADѩZ7IDRe i;Ԁ VELx⑾Q }*]UwjzԔ7" R}Bx(*rZA?uS|rFNH }DjY@ 208ӕއOޔEi mvR$3?JiIw %}҉ x_F1U,=<­')g3;˧MtSЎO=s]&kGGJ2Ko'8TuQ&쑒+.fʌ}+BUcMF͘)u, N2*W)D8"B?ZS)%toww*çrh154 $< z?"E;Ow2hteY6w{-\ZJ:bP }ZYHP O"Gw2}zq?_jJ`JWn@l>©vr=c5rUҖ!!J$ErjzzeS*IZS:P(MrS湬JqMk`4Weu=CדXzZb:ԱaݣDYә Fh2C08#< mm8=XM&Zeit$dPpKn\UkE0 g]:E{Rxj:ymA[YU:"8P֠@ 䚫u*Q BW֬ydg5iMi:ɐu?ᐆB2=kmxa hxc^t@Gaƺa;#+ꎒFY+ThIlg\[ccVWJx9v_D.%c]kM#9++#R;%LÓZ(w8 ]i]{c^Kf̓o9+.[3Fs:rïzv!iX295LdUDމzmV{Hʀ< eɌFs#g=O_=1U5vu6m`,cd 0Y=vX 6;_l =Ȉ gzۇAZ!ddc?Ja0A{>*2rV!7/֖MFrѹ}ihܾn_ZZ(7/֐Х3I;rѹ}iy<@ܾGZ9nq\bklv0Rق2U,A81 t-"Y!beL(#֦ˍ:X˹\=Δ&9rpQZbտ7(쎆]VWm~,pK S7o&PI1RjK~oJmtRO=F3]Ǚ,q.Fɮ3ڼIσB?MJl֜o$iC{SxEqI 9IuJƿ/z snjڬmXP`[œo֧\sH ӵɀ9*395cjh+\_y ~+d c+ダy4tPb pz+<FB|`a9k,L#J(wp@ >xg؎AR.WrQt]i١G\iR@ǽR"-Ƽ._z]t6L N46\~H vVZ70 Դw *%t<jM?2jbK'.jw"u=5.-` {!wbF aVWW㓂>X>l!#nzҬm=Hty4L ұgbB+BF&ڠMǸܟq WM*9a I"e$iD<v~yGAj֦i@I,[M+=ʑDVZծ7j@?pƴmآI00sJ[j,dAUrwХ6(BrUY>=^%3`vzMGAµV/QEQEQEQEQE$< ׭`kR.sG$c%:m&]7{sƀ0m#/ ,{ISxXVQc9>on}i98m( X?]MWnGՀ:1rg t-<B$4jV9aAr*̑!NJ6hxsgzrҊ\wg@ S@9$t\j?x']GnO¸.C<+-Ԍ=J׻՘b5#5a$x$$+#l>gDInܚ-W͑5^ƪdžO& `b;mJLt9@Fy隠o9-ךo*DXfenOji>f9&27sHy8=DT1Qd.qOBDh;R>K)=+&Xd?1qlN8 a#ҪOnhR[wekV:wL|QmʹJH䟭cZ;xeW=]:@+C@v r3PecHǩvl+I_Ju{i6+״:Eaq:$SO4r Eh}>+"aOBzVng8xn%,!sҵZkܔOCP[AEBY6]9h7`ޅe87cZd޳"HRUݷ Tsf (!GJH-que;NzfE$ z3TIWUVPNg\ިda9*x8Y7McKhs@oeؖ WhR bv:Se8`@ȧ!OwU큂0=k6 D+Cȟθ[I/Ii $EAW%yY]NH9Y28^}$kAGkO $Pk[S>e; -cTܓR'|֖4N7y&ۈF9Hac628JmlQXA2BĨI,8ݻǚm!prwaT,@.zZ 30*R@&:>".ma$k9QZӵK;:5VWTLʔ8+[YuH8(̃ #(=pjUU:sYOkhZ!Ϧ=?O.`8ʧ  ۫l#dgFrW[vhCa|{W5 k8+xel2==&^"lU~f5װoK+6Bwzk0Tb6>߻o*}!2F3}gGT_2v:G'*x&8xUq3|ҳ8\t'A?pt㵱%)k#*pVӊTF/{0ͩG [.7jq0 U;xZ2'I1R8Ny@+Q:=NQڕhh6益>'lUܱrCmu+7$ھmm^)cm b B-,$G׷1{IlkKwVIǫ!chlp[DU(%H~S>9ZD !7^.o-崚;p81aQj I"W @oAY7"Ffk>{G5׾YB SVӒ[& ˯#5ZsbhdA`2*++a -(֊FL:穬o'$νefOQ =MzPIϓ''vwU ,8z$+;N edv:0͝EӃ4+9ifI2øE1gSV }el $?pjjzO5]p9bءqI#8ST,Uv!¤L!w=Q1(qVHZ@Xr;P+Z3D+֚,csԞ}`An=L9< ʸl ޠejJT&OQ☬uu'5[Y|c$u?fqjcL(v(aZO*n`G5Aac%h~R1Uך4ϻ8+BNE+r'QFnsT/bh$ǥVUKpyĀ8Bri]B*9,m\&MDu+d͵VVX-"x}vކJ*FW)*JD\Nһ4Lw<|a5*:Smy[BzƋmVANFrJG5GHv^cʸPq8m'U1Hhic=oԞ[VbUM^<aucAF\=[zgoid_JGpgp^bBEA `r&{;}HF6or`үԃߚfY vk8 Vv`/j=H QXxI[ʞP\T(Ktu9-g.8 r#Prg+s6J ݌VCΩ{Wys\7 W:/,}FJ(ob;}jD!hժ,q]`\<;5 s,$jr|Wxs2NJZs^Exa㑊e1=9-Ob*1W8a}j#ʬYsP\"+P3[mo$e+/NEkUDO3>e7yAhOrX>#AIN~[0L΄6G"ea!dƍ=V)_@ӆbUyf]R ?*GHj[̧21N91SE>a݋wzYrxB\ÊRc/C4u dc۟*y?,Zs 'SDfzoIjk*\ރrǮjkM&3EZ՞CT4*ۨ $ڑĉc p C##,ZP~9IsY y7'>G:\!3ڰOHd>~PRE3 RT`2)e^:0j$bY Sw$u'JrܱUd-p:1 .œ=M_b'v檀.(m/rx#֮k̦f`Oֳ-ˁ %YI\(,5vwjt#"6XvqpcӚ`+`]XriLȬY牄so6iXf9[<HsҪ+T3Lm:Cqq⫽U5-ӼN4ѓfMNfi湭IۯZ&KA?W]YYF`z1`6~î0+y-pʮxh~a=}0M:ץNaU5ݥscRim)GMt[_c #0ԣ,e"y;'Ҕެe%.=: |w)}oM"c{F]Wo5Ԅ(D^y'h|{`FQ!P3?jrMkdt%(.kCH&hBzvkgd}ԯrdJbzj %~̅6{U?jӭX.،`Hjȅ# Bӗ*;Hۂ\6} 2c6"1bQf@twG JQq?NsR_2>@饼r#8)44U`䎣Zf-ܭsZiNA"F&GȌ*l#IVV_zg{8Fwpcﴹ LEr gxZvڍ +p+OSuc#I2˻y4!]\˺WƣrK)fɯCH#Ӭq_%Z).a:לk8Σ<{sb<8?]sߺ ~ Lù/]7WڮZUb26 pjk\Ђ(xʔ}ե`6kDHAɮoP志'@~T#M*UQe\I326k2Ev|u5 W[:t" T6 a]J9waqw&n\ćӓV,`]YʮɠܨLv%,1 v/:P%\t[T#!oa]-WjDdp1] C&=A2t/ǡJM滝8^) )g3Zr "ooG,w[.Uu=jZn,5!-0ڡ~o2!JiQ_òXսVYm'/%U:l xu:UH`vzCwl/,=Z[چ@W5;YXq3G3&#71ӚчIkm̎2={yc@̣!}i4i8ِKm[#Qyf` ձ;<,M+* lonf3*)UϷhX`)^ics [`+B,@b8KElŁеZUSKpj o&qZ[Դ4$= "c;y -K"U ts@T$q,SM!cjȍ1k_ is$2,ŒqL𽭢mَrT`UZ9{9}NV+AW[hx S=2 : vuǩY(sC )Ja"gFc7UzS\b #vߦiL$/I1 Fn954BH4Z] ǚQdv~v;iIlَ=Q4PW:VCSx搪$e&jv9% sxIGɩ^j(=z9i+jDcյ * U˒I5d9}3ڴMҔBWco$n"̷.f瑢,=l $m,$Ԥ آ{Ȅ 8s[nnMp,Inp_ lLŰuMvךĊ\`ZVͷtDQmnYWtU0: 6I9W{#S4ڑ1c]1]܎5LJsҵ(Mr%8UsֱnrbӑT<~ +4)lxqdzagF?o~~I@of1\4Ghgֻ`tG;xR1$ Ѽ3]n@;*~]:Sryں,Z iwϾis hprt *nnLitZ}M2f[jftmc֣^ٱ .ֳa.^:s]4 `эxDZJɥG:_0sZ,LJ0R#&N1?Ҙ]Ԕlt\X4dIUlԐu8#j] qT.O}+>r"HIb9si Xnq绁c"VXcZ@@#[xDs|s=GcMb7lcpϭ\AtS~\E(au`#ULnG3iz4k,g"⻼@IrXQ$θC~{ q҇i|ʹ%$@z֦4gr2{8Vi+4@4By|`jHyp˛UdhgPU<~y3 FHVb ,J` g0=~Qa?shX̪ǀrƦ{;)|NU(*)p||RCr2.lj9Bs|VqW-2=P;[NM _a^Ź/50I q׶'x$w HI#$0u==DNjuʻ$ 9UgMgPE=Up953кue5e_3Pb{VFfSׂVa=qRȋdsH_ܛ+W8QG3ITjytb$נqU`fRiI;ҩҐbH3 n[.>lvH߈x,.㽏'h*e8Sz3vgdh`O֨}*$0\=j X#Vi*HBi1.0J$y j9(08T*gM~Niـz:f-|#,mdt4m沔R?(]LFzڦy z V\fEi'U.>W$wV:+2hZ|siFj5I`2DxzӴ 푑TqIk WMD eڵ nc ֟u 3ˉc=2)0[s! =;ք3H9گƕBӱ ܞ_BָOīO8bE|.E8G,#NhCڗ{;_ܳrH ؚf ֮X?3TJ\!;Gj [#?=+9Y+vZZ&0XaF*6%Σ?o#>2*$\NQP:С}NCryF\vfF쉳FKKO~$ Տr}ZVg+&Tqi 4C9hqER(R1rrIhΡ wLQXG_uU5eN)ݗRV+nd]ƧԞLgnL{56XG/T pkTsAQ0}jhrݸ4+iɁ #ƥi*KU'8A[iktK8,jc5ч.vShasPSI8QBW85"7׿D sQsFs\'C6H;#\C^.L:ss(4óy^\OU1kҳs:*j>]~B~>(T] kRy] Ir^/?=nDMnt4[i&&tK*r)m9Xd!Cz߂6rvٮ9[@OR3=ꐏ$ԋx'14V \esS%v3Y=:*|})MZ%@OEm!r 0=֡\ZIAϥfiDl; QcC:ǐ 9暮 Wb4Xd`{P7l th؇vǽtyRnjnQXqNH}ꔴ!M9L'$Ny1ҳr{MVg"1@.9Kdz})w9ܝTarX9n-E(##>~JHRGy>Kq'oZdZtm m ЇR1f Ԧ$g=&*&op@8>TcvsQuaEenASJSMIa`Ƒ&䛆2k9⊁ٗ|=8͸Pi\\ PHDg#$uN8#>  j?ڬjWku(6%7$Ld=035(`܈#bFqL֓2̱ngnkDͱڬ ъ0#PH{ X~ԮK!tB !SN/Ԝ{SV|WNgd?ν BRG?y̙kXsΦoCJ{*xLlA"D`'Qgj!_6 vyM=8&o)(%0%#+սnZXc v/fc՘'ֶtKM3EX#F(n;ܪ (B/?0j#pOcjif-84lcl|1ykNI2/iW:+YPcZQm))(l~mҞ."BW.Zp2}; ๒ 8\0jÊ HVU"1V=E+]I5W;}(n.m[jHۖ=JCByP( >-V8 mMԣIr)ŻKBk⫍5 ȍ =QV%T̓s[.aE[eye&.jQjKII"(m3 YI9+'{Uw~IB7#Jѷ`Iog$:dKp*ans-7sBJrkFdz V-/VegJ[PI$u @& pOrvW_ݵ~Qtc(^x?Z&Uw?%:nuSFHwzsU,*煻M[Ն({-{R2iˌ)ݎN*oȲܧ*ֹ0&1-=jކ. d嗁~8]8EpA>U="|ɕe$ah1ʻy:?֗#JÕMlQtP֊3FOه;/QG97y^ #3noARDxړ(vZ1Ǹ.Ow#RsunVnсՄ}Ƞ 7jݷ!'Z6(j¢IdiFU;9=h ?ZFj4dБH f@O>[$ԃVYL>1q#XV'<16:"4ΈqP#'֠s\_[|KFvLN**֯|х?F\)BZ\Is:|#18s`ԡفxiZMXdSeN+5:r@w9p&ytAySߵ6WeMB_*<)!&g AR)$U lԂtpW=_ka%TJѺT±vKPd=\ia=M;ko"RFy>ųC8=I&uNmNF .06u |H*cteZ@w'*!z(ԺƲ Zelzڱ!}jM0ţqme q /|u>n ڙvӴ)V;v% -= ~We-2۪ڲ `+Er}+;>Kt Zh.'kdX ?k7o{$E rzļm F5_^|:\vЂS@s涣5vg6c~p?Wկ4;RV˺tnYCzU=!mmrHkc,2'z}yH;{@mr)E2/O8Hqe\|ꆝm @I#OhUq dd\Sȸ r~֚uuR:d9Ôy>j\@_Jz`f 4JP8##+f]1뽙zDM0"!L*?}-  o Ux8]A/uIY8v @>jӚTK9F M)zk%"aiI1E&7oL)Zwn4( .rG֕;c;Kw#ˑs鞕sm$Dܧ \޷k7u! ǶE#W>~U.oQdؐ1:Rp>MpsNks6? i绒{9rҶbܤQh ?D8(pJ0Xֆsfuc.J(p`vok&g*ռ#fݎubޠi?UnnhQЦ]V\Ŀ9 OZ1{m"fYQǹet9"Hdݞ9 Ѕ9jwJ47G ?wIS[SH]B7G b[+ZuQ 1D% ;7BZ^,;չ\jB,좻ե3Uן!-TzӓMd+6(u 2F3qVB9ls;Fc@&XGxwXK%̪Y9?֕ uk?ݦ6HWՈ ?&=\:'3ré#&1Zw\,-jNGST Y8Pr+DId| ^)e`MeIʑăj"[' V wS$H(iƤ8Pi<ѕeMgߤ,זcښW& RKMy="|y=*2/O t6H%srQ61VSEv}ǽ ~m˙fCb8`uMs8:ʅp[?\Kټ#0z+uׇ[Dzx&u%O8GQThN`8kծtLLDɕ ~/[=#ČޣdMpFT/4U$cI4Znn4$8;=x\N8R==EcsV[{gcb'%'diIҫL#|X?*A&"2q^pYy+{@]`o]LK;zTCKo-n=)cXK`:SpEE=w4؎ZpvqsPM$ִ50~.A!d78jahn)Eb7+vIi  'Z7HOɢ:{Gd4XM NjeNM"]6SOQS}5{/nFiusO+faV\n>aQD6L3sY=Mp$[{o 1R;(B3T/"0H6I/z~G@:ҫ+Jw&2M8UX^u qZ7љl(9ܤL5f*q!j6p:d` geA!㊙<{(ONWv'6R/پO8\J+[-\@K`Æ98-w*KZ݆7 c=I<[^"kR˱7B*$Tٛ⡱w,2p}j&[qIDVm]^t$ujQi2 @7gҨ =J+0خwksge##Xv6NrY򤕓bp]&زF $Go kX' $\U'd3o.@Uc\>gw3U _rZQN_Ҋ".δNB܎@AF8E@KJmGn =^!%2cNR8cG:2BhF:SlR8'֙nťԀ$IYYA9~=pUjխ9imI+Kd6E5}8+eSRYnt׍lҸ#x^u21p5^nCWv{272m MJQ,XA˃)JShFJ贺%ih2DLMpksA)dveNrF00?ϽZ1ÈFgc*0=*$řqZf h8JY0.*kx ?ݩ2!7qa ԋ4Y /< D^Sҕ>i2gk'Rf ٗV!UҸޭYw ;=*YHuǭQ.n>qU-+(~ᙺF8RBcdb1ҏ%c+sZʊZfotoxx-15.11.1/data/images/select-area.jpg0000664000175000017500000007173712616075370016744 0ustar micomicoJFIF0ExifMM*bj(1ri%gnome-screenshot0230ґ01002015:06:07 17:31:03Fotoxx:resize|tonemap|NE http://ns.adobe.com/xap/1.0/ 352 516 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((4" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?R$Q*^uj>`O֪ixGOV>ћ}~z\XWi%`+&E2/ҏ?9i*?\W IU^ 9Vy##kj5}SCUt޶4elaU+) $G`~'0Ui+n8YڕQjM|CV@rMis ̤+UeoVvgs3LD*H%Hs߀+HIZ..x_ʼYC.syr.;YwEBcB񎿩,v߶5yj =C:HU%IB*M*};7Z%:r֦ݴ?';I8x= .P"\΀ Ld9PLfz/F迕yhYg5Zg_޶B Ҷ&kJS47n-PxEqz/F迕x| %]/I76~g]7tɴ5'3Ej0K9(X}* 'h~2֭tgԾW6gY=KL5FeOMNhXEeJ0iwETߑiwZmfLx[5k{4彘<>a'yF:QM=A@ W|QM{k%$uVPh"5ŴJ 'ߜ1ֆ>S"dWH[%JA?x_ʼM4/+h̲)!O/FTrUm&H.a}>٢2e+÷]G]/4i`ݿֺڟ_ʎPhe-2m_9[Z[I=-T'jq*6.rV{%̗Vp("A43V8+(.c ;~O/FTrJAo;6TW7~Vvy;s֠ob]ZDŒ")=q]Tl_/G(\!Ҵ c}6,/F=TEZ *[3<鞵l_/FTr8쭣!1Dk_bq*6/.dyyQ2<<ؿ_ʍ mmk_/Ksͣͭؿ_ʎP־TmO/G(\hk_jq*6.dyy?SQ2<<ڟ_ʍ mmkO/FTȑ66jq*9BGGZSQ?s#ͣͭ}ڟ_ʎP־TmO/G(\hk_jq*6.dyy?SQ0J+p_ʊ\:($(\QJ)qF((b\QJ)qF((()GZJQ@CixkVkSi$ [BN5P,,.^d[ nI]YiMvFff}Y|72O6HwI1t:rc%Ҧt) ߿fp1O4Nu`60rnNC9?\V3A[&Yć2mۀzg|`R5.aZuOH44I)b%fRCpOGĚw`lJV1+ȥ\NzE{n$[IVh@v]Fz>/j7q87ݴzrv^3t]([IK5_^=獤q8JCӟAmgY<Jq߭QRHdӖW5O^aGVأx\՝`=׌:axZMzH,`k,C09'ڻ &OMvU!*6 ZzEy_¶qQCrĄL8I[!&ڡNp9"ZNȊMG)("VpA]4+ۘ.,s2FIMʿt6/l? 8QK s1Ҟתס}[=.^hb\r>M*[\PŘ$$ԆQEQEQEQEQEQEQEQEQEQEQEVap֑,2; 8U~ sV'V@YG8I?QeI8^@o!U2kǵLhz&]=dYڮii=ټn<+mu;+=8Z7,!XV*Oj]j҂M Pw!T8=5<ӞGyƩwVz\ 5,JYY,мKk5CpxٳX-σftV[mIw\#Z9Ps ~Kqk6}3Jӌ2D::3p\nj<xo,Hiնdcu-KjugX]M4i#D%aMp6~Oqi0G rڕ|g86~4Y]IweЬЗntv8ʅ#?Gs=7ĖvvZ=hy^bKs8{ KN4}J3ama"ByL1lqs隞iַ/walF3 בx3H<;qaALo"U[$=jρ<-:ƣo4Lq"&A~b~bT=w-?ݍK¤=jM21+C%mZEֈd srב$slnV\\~u ct:8>t4KґIǾԯ]k[^$HwC a$t5HhOD$TF=3ڽèg,]A+D) "mD{!9-DLnJr+zzƹMEt:qUi5eVB\uWmSZhldu"Hd cVώxfu7ZSԟƩoUmy mv-qJO?VZ\I<:r[Vznc9j::\9o,F]. +]^@fMn^NG%SƮ^h-+hmGyb Tt$4X@=>!hQ4K4J ƅʿԁ3W|5 ;WI sn.XʊBր:<\Ϗkmm):=ȍ|Rwn@emEum4s*Џj9ھ_oۜ=q@KX s/ \!gj~VⰶKi Ds?0\1?-AfhN;iA O: a3p:IThsvF1RrJxڗtԳI{*9VC^⎶=um{okMjDd<]XwṀ\3C}CƊOlBm* dO5@Fy1 ?Θ>!iéKsg$:lǐQNݪwrz~`;+?dӵ%KZC(y>|}>_VOmwJH$s `\M]g6B$dtM#lmbG@>f`D2rXy7?(1;/Id3C:8$t lPEPEPEPEPEPEPEPQ`G#G坃$`qԵ6D)f? M!S82?SU"M,єb;g9u#4 "+ 2(z4XhO !%Hw2LgwݱU. (%=P]L]Q40k,6>8:U[{|F$2qj.Ɔzo`/~ag}n//s!c`&35x#M3uG.U?_k\ / j QK˻FYIʌ `Z˺q}$P/#+~/ufbyZ\db N;泡oGjjeD~cNpWmQ46ycI PI9@߇hzʖ]PBu!Fk4ˉb"~T}Ihm3O,qD1v 4^aq}xs1?1/"- E 9KƑэ'/Qݖi]4c{# !F9'sϭmsF(|6{y)~K"Yj߀˙/d{ՖHx*׎b9Ү,no[tξlcq+[Þ5Nɦi/HA \IJ%7ץkiD» ;;Mm}m.RbFo189=O/Ҽ>OkXp5LhTut4U[)v )(ZSIEL7q弉A9qV;-Kv"?SM4V;Em4\.Qѕdzg#cLݼ˨jS ki-!WBj(Ru΃iMsvK7\]> V&{ƋNkزb͒gEc< gⰞCnu [h` (>iIjdq-rr`~R@zg5G}yֻM*IΣo<;G$hNJ 23G%ҍA5'Լ( qWYE@5u8~{!a|CB㚘x9tCR;|Hr6|5Q@-?m469VPEPEPEPEPEPEPEPP@oKaǍ,Rnp#Њɕ "K 3]UH "nc K,qh>U=P-Zh%H"haVbهGVò_Z+ɰ$6^@ *z:F0``P+57 Es8Eh]3(Q;5=ܱ[ItI<Ӛ1!@')6ڸ=F:3&t>=M3ʹl i 4kPY%AВi{.ݸQ+=(pcȼI%MlyM4"dl&1RMHx ٚi5D~R'e$w";W=s=v8Oz_isIjVs-Aiֺ2kN+WIJ|<09Q'Sps+Kxz9#.4ț $ `TwCq&..7K> @v6d{9{psӽ(bBO|P34:|1j4زOs #<wjΏZxzIZ(U!P+? PQ?Ƒs&qъvHMt7mr=({QCQEQEQEQEQEQKJ)qF(((((((((((w2!IeV8s:AՊ33z[?@ΦTfM;PV1 5z7YcY#;AtTmbXfO0r3‡-MJC3Ҽ}>!k6.ӼSjP6 A`X}Hɮ:S)$J*LFUҁ\A%P)EbF3CBWv=jkv Ixit(UP2k0\+𶻨 HV0VFFW2 q:_:QiֲCyf]'L}1zu.}o5&Knmԧ8?E|R dۚ]oU_.mR=`1nxr5O_/ 2k:uԺAݘ98?N$+p1x7 U[;?gy8R9!R>04.UOV >/1?_=F潣-B[yl/2s]'nq_m 8mg>h [ߵsoK3>+C΃Y>nHvۍ͞}-ԑ[ۢ$>W&7s u'CR},ވф] d1‡JZJM((((((((7kǵjzcy,2nvz@7) oᶒ'D8IA>բx@$8?KkK `@ǚz/hʰDB3+&7JQj:P+J2l "D>\+(YV([ 4,Q(PF(> Ǐh^<5?s/pbѢ]3(S9T:{T.e[i.RT쥏 3>׳qX9FOH!+(=m?Zs~$gWRvoa,>` +W孌Z8(̥Xڊ!aEٌm 0"s}hq=p h<K;Sb+_sI}K&2=iUERH"I6t~'xǺWttدbrǜ)ziO].p1][شJD!G|)vl 80h>uVXf< PPv[* I } xk[Oe;{A; ܄ۂQ q `:ӌQӖG__;ygi),%cZM6gEpwr*=nĚTjsIm>-T41vǞz׭in(kVC]Ui yG7I(ީ]̿p1BC> vj| 7Y#`euy?=+d$\0!24,TdPV.Qxdxv)x^u,)688o^M{1p !(;QOa5wk5>"6M3[ܡI aOc PeconmZmN9^\cY Zqӑ}_YPK$B,1F3Ұ?xfK`Kp$g1^ʌ0 TH*F Jן<` Yj-Wma#1GrhOPB w(0(ZJ)qF((b\QJ)qF((%QEQEQEQE[T%Ȑ+)&5kbB]S۹>utʺG3Z%f[9'mDV}'~/3F+ukv?]k3Y="VEpvHuR-W]1#҇ WolF+ )#v+ůf!Gn_&bO+,MVݺY4fѷlǬu5hbO+,M2MFܼd% /f4f'n_&+,MG3EWs}Ku)DqɽD)tTJ((((*dXPI,q*jjI%CdI4y`[h[YiFЪ,R2'p}R:VgF2A3p(&KhiVI "|t ֆ-Ri6^B? 韭1^o[>KcRvo*7ۑC6'fZEwnnD#w\qƻ{=J{w`kp H4s>f$V =Ahr?j:\WP9^ORzZ7^!ύ5 *4xf~뮒Kg{k8"8tQR 1w=еY6 Σ'$yī+M{ybXЕ`6,91WǺuKNP[3!6} + "AbH/@܄>-އe$"kqmy*AH<=)?5fp1]'ؙʳ9IMk^CZRH@æ{xW6$ڄ2y-k [,0cB;J 2IR, Di\ޡit/[MD"Kv,j2=ϥw3Emuxq"Jզ\WK rOS٣`j?ݿZ>g?vhMu})>g?vi^Y)~h\v  7c#+i沷mrKH#b9]=m}~Ŀ4A^k;U DV"k6E*e6CG+Ѣ'U*+{9ou;$_ЏZfq],mb p'v;En1j_{f?@jOw2I2d?OE-UM@Zj($CXp~eO?Vd1?%n5D kr&ٓ`Ahh5"R7fb͌~DhX gd\}~-=U'5N<'\M–giy jisO= =fxzd7*2F`$մks~sw5NTr'X(} vnwqu:DAS!U jڦPȳjm, 2`1D`Zy&U0|Mfx|)Ⱥٶz|e㋋^i4{5T/+`,'}+_wWWj7׭#]#[AiWw{&:VҺRX-5MEbD E刍y#$db3yޖ;JliV{G̏p=]ui=.T+4X@MŰz |>-B 8燬>`ӷ3f}Ķ3^,7Z[MytL-;N몢9;O6>d>hSA'Nұ|%9u+Cf_a[<701?þ]5Y.xGn%"UxR2P9+09hBz+] 1%h(qh<#E5[e󼷖mcҷ\Hc9% A{?:7>i /,NU%956mYo, Uy?Zs@/.=?.9çBIib%t-O>5QMռvPJ{R NX`N(IFIb?i iI.i9Ѓ NӼԖIf,|ncuRc_а4o}FVӡtHMDrq2jѰ<,Mk<9=F)7> '.I?z%xkšqp^kLUFr{Jk@_9K0;$\#?^ .6d6I|P,+*+Cgy,g0?Ӄ@MMa{ (;:P̫TS~~ǃ^y7(YᕤpT@FO˭3Z|.bÏ\WnbIURğeIoold $48eӮe^ ~NҀblaU}?︿7>2-ROL\F>Ӄ0pkzZisi*Ov_(r?Etq}4Y4>FIoewk :0p2 T߀Z>!&Iogh)%FFJFB R4{i!kök7L~U3KuX{AjL sz7>}?︿3HnE+?md`q&$N޼s#K9&3[Esa3vt{q}Ѿ_]p-%kӞԺ2_ecvy]M\ET@T ((((Ebh5kN#ȱ 21[b<+Qt~;x/Ifk!!@_;ⅸ=Λy[Z3 ?#گxAN{^0l/@~8?_cӮ?owq׿R>CͶW܎Sx͎Ct[Kl#cvӃӡZAX:b_v$0mUf~Mu$:n/C0!9 7S¥Rή|VgV33_jiiޑs}G]?z i>ѬuQ҇kwWin3g5݆7No\V- j]9d`Դ{A ]}츼Wa u-r-7 =R[u67 *0Oc f<ּaki%& >=;᝵ί ( 9q+K~5 g1GAɞ2C}SF*eǬȴFwj  i/ C٢'i  k Kku OPmm݁Y UpXKy[qʉ0 9C o<>u k4JF{{H =`Y|=[{h@U5Im#R6 򃞹?JKE:5kloY&.1SGk\q7H@ӿyٗSǾ7$q>ֻnԣ>SD6쎢'Fxzb"[:O'}^=6`yɆG\gtxE6~Uܽ9ScO[x}jE9KAd#bo9R[j3m3K{L{OV-P/Cʠ\ mb=_UzǜJ?Z; ^h{_)ln좻E;9Ϥ1yu xV_\|,q[8s.D biZX6#m5[dcK-!o)5CǷX"ԯ&Hf-I20|Vt+n#[Ɖ8pJti-n3HH@r:+?*چ-e-vsv}0h_YizC,OEi3C$AQ˸ ޼u{MkIOwثF5?\k9SmE+nh66mHQ'hT y9#׏=56ПTʝdcuzmŴ#8fsQ]MLqպ&w]cz҃)L / 1ѱPh_Gë lT"7rISo'ZoœhL..|yh#o}T'`e֋ڬy]r7㞹ZowZve2]i!ԝH# ZBOt_XWú-Ũ9w(GrlZZB?_M$) ((((((()qMe ?"_WF)RƏ)}_?@yK<?b寫gh4QL4yKM/A&0Z"9_J!us_W/gh.?+y~t}~B?U-}_?G}Z%RrfeGRƏ)}_?@Ο0LF߫!4}7W<Rƀ)7G.?+y~us_W寫gh.;ꗟ?EX[vg$0F8RyK<qA[RƏ)}_?@>?}R!%XKu}rK<,4r TG^"l~,-}_?G} ` ߸\4yK _~./nNo_W/ghvVwWYeUǡA|Zƀ)?n\V甾}_W?\Vo?\4yK1q}u:/gh4Qg}_WF)RƏ)}_?@yK<?b/gh4Qg}_WF)RƏ)}_?@)}_?G}QE2đbc8jQYz'e(m2,G-'Rn5]igMxiO_^Y Z@;P;k.c,nx γxM8# ]KݕZ.}N㟥 ].z {hd`0UHSF$~SQ6Y^?i ?.&|5J\Ǭn<(ns{'V㰴Zѯ%C w;P_חz>]iwAu+ d A 7of6O{IܪF㌚W߅.P12pb5`ˎn3NIbGUvP V@1*t+Dܶ1y8M'>]iך/t[ -ڔ4r]c૶]iח54=QtRGoS٥)se P H\38 i[}.z>]igMxC@&կwO"+ 2s[~9o .k+Fi,̳ܕ&`v! ~އg/_&tC4Op#"ڜ0=q]|͝*0 |+J:Qayq0O O_j-=ix5[-Rm]' )gM˒b>psk \؅#}E [GR9ԭ딿RFq/po iMh˿?j|d~$ӥ),!X8 ~"#K[\b2]hZ_ւ{^_z>igMys|Bm&M< v.qn #2Ow?N7N^C w"hbWog-A~O:Wgެq(I dQ{MO]S$+)sRݕƕݍ?|?n4k4x-ݻ#FeV,AX8Lڵ$ \@5v_%;]?O}?.4k{G6ַqiט[_ӭn4MNK|U 2s|DֆKtk)Ve>_zukn%̒ld )Zς Q]<2+YI?Zw(1Ee[h^<3&WRIx#ӥa'XɎ)$DX\_ƤKME@JOj H"#%"QG ދ li.:.G8њe yEaEfR@z|wztVom*ڠO dCSgO}nj&h|=c3@SjDsѳgQEqEyO,l]њoFD4b!V Gs? 4'7c![Y4Q[f2l4zoJrNKmRHZ k˭*INEV7Js^";ϧ3ƥBUOPaZ4Ò$)"찌D#">/RMqt3>%cl#.cȭњÒ_A!^s!eKGL,(2Ga Vhc֊940e@B=29?J`_Merҭf{Eesp/,$W`v3?/Y/bvxQfX;A3@s.6ɦ Gl?J?H4MQVPF9FhMt =rGB}dAn^ѺhR.VHw4QW@%gio%[[pcF Uњ•=-ٹi/uL{{4ZEMYG@s{Vhan)7%oKEhVӷm쬆Gi6by&dKKu<b3Fh eBt?(X)z{tEgm4JoњŖAnf}.K?\}Nz"˦$$`'#lf:X/i'ȃ<i-%T2j|њǾѯQSQ)0.AiA.-Hǒ lzVhbqF,oKD2z?JG@{SlJkbDFSwޙњ7VOdzi/ۯʠ` 4fJ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?fotoxx-15.11.1/data/images/printer-calibrate.jpg0000664000175000017500000023141612616075370020156 0ustar micomicoJFIFExifII*   (12itPanasonicDMC-FZ1000Ver.2.12015:09:06 12:42:05PrintIM0250d  ' ''''^''''$NV"'00230^r  |o6576576570100`@ztR  P% 2015:09:06 12:42:052015:09:06 12:42:053 + Fotoxx:trim_rotate| Fotoxx:paste area| Fotoxx:paste area| Fotoxx:paste area|paste area|paste area|resize| Fotoxx:paste area|paste area|paste area|resize|write_text| Fotoxx:paste area|paste area|paste area|resize|write_text|resize|Panasonic1< !` "$%$k&0408'()q*+,-./01234k456789:;<=>?@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-15.11.1/data/images/select-finish.jpg0000644000175000017500000002537312616075370017305 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|tonemap| 4http://ns.adobe.com/xap/1.0/ 203 320 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?F*Zhi`O ̬R}Wuޙ3xBghQz\k{;$va6ǥ$"nBRyֹ,7UGeF o .uy59HƵ?g7ŵ[###Q䐑]qF#&Ӑ<[c)o! M͕}35D6##]~UcsYc7U@=.bzqK~Ug$6HZ!ī=8ؓz,S9ݚcql~g7v܌FMvM2% G2)#&od#tTĭ#@lhX~TnKkLx@Bov\%:iI!.u*ց?*7AW!Ԏo{h4vO=iujVW$ڜ# kpB#߰4\v;M~Tof@>4oE~ToEX=F?*ލ\ Ǡ=UѿދcxǠz7p,oxWFz.~ToEzʍ~U_7AQzʫY%p} ڊc+&sLdYdWK?*6/GJT/gM.]Y@!piH?ؿQ&ٜ'9sܥh_ʍSgYM8K?*6GE?~9>;$Np͜"5Ek8ȖU?3C׵l_ʍQ`qi7^g,!͗}_Jv (>πsl_ʍQ`d,k`<26GhaNȲ嗡ڷvGF\ƾtNE_9۽#IOs,Jؿr;t̫Wjt~TX.Q(*ؿ |<ʽ?*6GE22m_ʍQ`G̣̫jt~TX.Q(*ؿ |<ʽ?*6GE22m_ʍQ`G̣̫Wjt~TX.P2p~UQE⎵[kZηAgaoL"mč/$tb]R$3DQ^QłOWUkAEoIRZ}-BZGp7SxKicjw70Ң[Fi͂+,&p7\.p3G@7>rAxfy28q+}~.g1׆njk}'de^ xgG%lTkUk߭.fEw=-Jȍ֮xG[xd6 yލ*S0u8gb k`׭?M촒)p)-LK5İg5ܓZ"v0 ߩRd[ަwC_? sxgHkOEyJċn ǟki{wvb`0J%h;`VQ'4۝vS@- 27 tOGoy;e X>a랴ФȶFy]9Ss\K2l[X`g%t򰹑R_;8)(8[~ikIZB&)T 짚>n˜ җPd㨂}3ol.ygh֍[VѴG6Y6;e(H1Ϸjԃz$X<g`/9Mޝmkr]DP\ S򁜟:Sևݣy#=<;/-R4in""l |)8HLcK< ͉ >^ h*h hV`p3Hf|2>M̗[[lEH]0ri=g+Vt6m A&Dvϲ)t <'ҟS:(" 6#21NM jM\FΊ;`r\ Io'SK?פa'ڢ9]U l!%Ε# o@H8ӭi诪J-O# qQk[[R_vGo6dqg^!MPI[uF@q TCoWuyHH!zn^b3-!/h&7#hK}˭)iu=Z1c݇+$2M'ZD%H ˍkK&bpļJ:Mn:=h]Id]à,5K-R'gXc*}<Ёu̓ëHԙ-h.ɘSc׬-VmdfXar0;Wsn>x.mRKy=s,=q4n>s0h$2IOc+nK{PwuR0۸>Ѹq[Kzi]Cw# Ī6u{VEOLuq4n4X}npPM4//jw,dtǥl:]BG\2@6q+}hOSLV<+KO&TDVpѳ0gQ>[^V36%^N2Hє7k}h}M!5:k_|S Ƶ&ɵD;泃g֍ e xR$wm,kۉO] lU8=*!(iV (u<%buR {d:ǁ|3KrRcsҨfiV4 SI|b(u'Vb2mo7|aF wjknTrD$~Gk@gBk }'Jw5VOEf4ľGLUt,%`ۻeRX(((((((#]Xkr~jIl~puFh`wEvf@$]HDu6Md׮~k~4ْuFvPlAK;kKd `ȸ8*WE7gW \ew"o$n ;-h Fn4oc#ܒV}Ai58o;鬁| t^ioCF\-,i`M,$H٣hOA@94K ڬגO9:s bl1YӠh`0+I2 J2bȚ-1LUE<Ѩ v@22j_b ҏ^GHz cIAK&+:MYOޙ#YB#8$F}OjiH,ϧZ@ܕ+MxWey$g:#E$>(Q:(0((((((4Q@hP3EfE4Q@hPFh U[PQ*2}̫<,}?""PAMz}rs,0nb͵7 c? (l8f(Un Ź?\(è4ڮ-Fa0F ֟i+Oco3"G?R h((((((()c$QQYAZդX_giBMrXD]ҍh(u ( ( ( ebaF O%l??Ƣ[|22ɻҤ b M峫ƪv$(kTݎ7C(Aȍ3끊}QEQEQEQEQEQEQEt ?5R+ۿ+$Oҍpv Ȍt>d*En=%ne"DNSt774+L&ǥ@ G1T']%c; =FݳGv $L)o̸ ?*,5%ӏ剪Fm`&tMt1zvMɩ,Ƣf~e4p@ *"[O'ɨ(((((((((*NrS#SQAh7zKqAZ+$W'ͽ^j}vUByg*gklY2_]F)4} ]-ƨ]他OJ j9b/%K/#sTwr=+wNNE]?M]@]呋$B0Kgrq'NQ]L**3VzL h "fdq8=jޅj)Iu2ڄF}Ҫ kLX_\ăc=>e&N3MӚčG?qh> KizTVW9 o5I'u|CQI'u|CSyk&iKP{6kcF% 4]56n=:Կdw_?EɜE<;A }=DK0T(` ) ((((((((((((((((((fotoxx-15.11.1/data/images/HDR-adjust.jpg0000644000175000017500000004205512616075370016451 0ustar micomicoJFIFtExifMM*JR(1Zgnome-screenshot 339 354 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((3A" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?/ok#5 -!%FrsK$qOJ[G1!𻎣T@56w"ܨ g%rNqc1GRۛС.dxS^ T%Y!Vܨ[7:L1xRWV]25~"֟u9".bS"quF(ՏStX<I#yJp={^ go9ekC^EfmVuG^G@ FO$U =GOaV'z-#ľ_5 Jtg#mxG p(+ٮ|1y=H@lJp?W+w\AIn&;FC`E;y3>!\(2)`/ %{hLX^m &`WxΟؤre?#( '=:w`/ %HO22.נ|Q֐NľFЎ q[_M=K[}:;- [G*}lx< BIJw_ I{9Ҽ;-m!b%ؓ!r2x^xvH4&#z{ױxm>|MiZ-O0uo 7&О.l|A;y0O<})Z[[xMgY>!*HL [BU[}6Bm'{B<2>'޵Yl4/[4MIͅi>~s1 $ g R"C( ~_5 J,iƎ :=>Ic4KkKcoN0`^z}_K[#ЩRQЩRW5ح-L/9Kpt) ۃ^3BI`/ %`/ %X"'ЩRQЩRU(AWT)(T)*r +*k}*kb9s_5 J>_5 JE9`/ %`/ %XDxMxMQG"b< BIT7>֮tm'R,'I8!j~"FRӹ:M}QYT}=ozo>j]Fr[ɰx]Rh6FQ}hmx$sJ~\]Z~ڜ{wiz+6K MPbgIcqQa`8s\،5C1ˋ9AtVI{kcZx_;ޙEx7aDwct_:N}\ŝ*du)+'UK#v7{ڹgG?|?WzY<=ikƃOǮocZ?-Ͷ~"cX#i@kz.?<:<;Eh5&pJs+ͼчZդCіEY)OK_Ɠ#oc]o"q[X8-[QvG% Kÿ׿7#oc]o"q[X8-[QvG% Kÿ׿7#oc]o"q[X8-[QvG% Kÿ׿7#oc]o"q[X8-[QvG% Kÿ׿7#oc]o"q[X8-[QvG% Kÿ׿7#oc]o"q[X8-[QvG% Kÿ׿7#oc]Lf}oTc?=$:ĺ]4] Kÿ׿7#ocZ?fw ~8VN?Z?N,un-dƋ3Ixw:5W;ᮻuM}4>LnSy|)r${V`pPH\)Vƍ:KֺH>$g&( W ?GU_'+ޠQT)QEP@ ͼд˶/-BS-# U,Ԛto϶ܢ0QY)ށQsm5{H4jG-/Fm2ȠRq:e݌70.Te 5Svג)شhngn`>Qw4^/gF.W練zy{ҢMI\D 6¿sOnB8!3V$hH*REQEQEF`&$ 19\ 1fҤm>~#(ZN[+yn ;w"a9I`$et9}!{OijĘIEfj'*}GHB?x/ W ?GU,c袊@QEQE:ƌQFI')k%j&&f>@?14u#J^=nPMGN#UV]aȪ1_ϦȰk 6;bU;[}V6XX9T:0)P@$Џ$g⟨6!9Y!uxeYNA( ( (׶1ys$Kqހ,S'+x^YHAw`RMchjZe sw#F9?*H4L6<ڌw/؇P|"):N1ܧ7Cr2>@G#oqlBoI\c٨ P#u 4hmyɝF.'ѬR+3i@/_UElE"M+*rQEQEQEQEQEWҟI^^oJ'4QEP~OWA?GC$ ?!^ʥ}QH(+VVe&/ڱZnB[+z3\}4iAW'Pj$PHaTvhvؗ՚((@ 54RhŪ39(Ɍ}ElQ@ X%qe9Sh.t^}:>m`G>s`#;_J_[XC̑'AO08DCϸ}X#o]K%E=(u=K`[;s/WJKc3$c9 _>C%{VQEQEV4iR֜$ݎfHюt?^4PV][7Tuo.;X#X[!Fܒ}<$hRH]^')#ڟXE&+Z{;G1#XeIIbpʲ(QEQEQEQEuo.p(^';-]j(+[Ip"7Kb].wM1;֤R$0tae9P((+iO$CH7??z(G_'+ޠQW!}zDR>(P}ݤda 5PPnWh!^:qhm>شL1$\z{U(((b9$L[J=댬)>Unuf1.a׸F>itL#^Fz}hpNY죹<֭PEPXѦkخ!̱ȍ; E`H*FAk S@ۓ/lPC)@ EPEPEPEPYpp7/01}+^DY*ɴf][;/Z֠((O{l:SI=>'*}GHB?x/ W ?GU,c袊@V&x >)## oZon[hV=Uqyx*sy3'_çj@Q@W3y}6?5;ۍO oeU1ݏS+-m~Ǫ[+|ҏiҧӤyީ Gr}5@Bh ( ( (  cq :Fܙ$DOlѴrtaR2QXH4ӜV90zҶh((((+h+*,Nv坏ROY((((((MG*k+ =+")F`f}=ئc:٦MG*H"ץ%Da$f~Yz7rzV dހ(((( F͚E]1 yÞ>f;d-[ 2pA Qn!#F11@9 J)(e wO{l:SI=>'*}GHB?x/ W ?GU,d"Xb[1ؼt?O I )`Qx(G*H NWy+m3pҧޟ)ڀ=Ƨ3m;e9==s֕VvlzԓܟZ|o C,q }QEQEQEQEQEQEQEQE x +%ƊۧgsF>{mQ@ h"Yau6 }>kZFckBY}Vo!V d ( ( ( FAʷ7+n.)&?cJըdFjq$SCp1O\z4kiO$CH7??z(G_'+.dej^P#R:Goz\|Ѕ{^o)1/77t=AЊ5$FGXg' $bF 2I<YH%JisG9;>eҒC;kz9ETPtQEQEQEQEQEQEQEQEQEQEWO1*}=#?BiMޙ fc:+u[`1[O+<o睞@ 9Ao{iXWc#(G֥|Q\k,Sٟ?l7oʏ<4TgyGmP4TgyGmP4TgyGmP4TgyGmP4TgyGmP4TgyGmP4TgyGmP4TgyGmP4TgyGmP4TgyGmP4TgyGmP4TgyGmP4TgyGmP Ue<FAgylnO"H}ʷ~7oʏ<w:m5pL2TRZCq\:=ܑ]mRiX vב$F]x[OKgs$o?<F/P$EdgUŵ8$y"h}eaEiDI J6=,[h[MV[[X0͒?5jVm-5k]ጐs8\g ,[Wu`Б(O*s~/--u}69dU?]'^k?̿Vτc"gsg wGrrf .c s>B!tk}}7Cl1x^˭Y"\Cln{sS'4x崎KU^s+}Uf{([&3ynTOαo<wwV(\mR6 R\xGRKkuoc;Y%߷h8a z S}h@ʊ02}ii (((((((((((I F+Ip"N=9*HKkȮ) yj#T.<5bup=chz^ r+ۈⷲkADyLI0=;`zI6W;(|ajV6 3lW x!Ҭ5cy}W{UB  d&CN'[IdFbc6{Eu_YvWu2HN$g`uxĩU01dP_!xqK'4=ֹmKWD\6mork{qޥ3GouI70n#FnQjlxM .M,G,c޹HWNS/+ I+?}QT?'otٹWE "ىKK ^= .qp14*=*-QfDQ+ [.j}o< gk Im12gIɂe,ٙIONJo#Yխ̶J1 T:"br.pHҼx;\kKM8iwcr~JFz<1Z?"hlu F[nڪr@29<-]֩ck ijn;\E7kДCcoF@ی^_>5iajVv2-Ύr,ۉRHA vmE ?Cl44~aE/3鞂 urHYt$ZF\8x洧-6郐@QڼYe,ɉ,&MǑ9q6q$_ 3CZ};xEO@BOjc7"H m+Qm&;=6\ZE_:OهI_'N}7JXT,C c7˴R+PC))iQEgjkj- q HA M!8—>RU }^xZY)oZ@4r8˕X_L{S-N[=660`ou_th)!#7@TL)ѢG*F @joŬh^̖ɴʣW9{U}[u͑8([~?Ͽ KP+K@!>ZY) QEK{vVl hUz MmuKoigeLE[D|h'#+MZ=ķ2h^.= ( ( ( ( ( ( ( ( ( ;IVN1j_ |[EUae|I먿W/u+2+{ya3EYˡ/=9_ae|I3/q5mJ3wemeDv,qHeT ʫɥ:6_iqwKgwI!a%*ąr,Z3J⶷-ue!GjԵJ+ƎFU93%5+SݥcX%| ۺ(nN@'*ŝm u JYxpyK:WбƏcCG4W9 XⷛTԒn. d0I'y`یr9n?#{KбƥE՞RݭsJ̹DZX}noQEQEQEQEQEQEQEQEQEWJ/Ѕzuy$S~?P(G؟#*|0NOW}/蟝yGc@?~w{{~B2E=Dmo>ߐ?hz'G"Ϸ(}!@/蟝hz'So>ߐyC~t}/蟝M~B"~u6 7oP?hz'G"Ϸ(}!@/蟝hz'So>ߐyC~t}/蟝M~B"~u6 7oP?hz'G"Ϸ(}!@/蟝hz'So>ߐyC~t}/蟝M~B"~u6 7oP?hz'G"դWPn;ʗP?E=D/?ʗP?E=D\ Vo" ci1J)P.O&v㎽VD_?:>OήyR<SD_?:>OήyR<SD_?:_0Q09zmXRG,p!$|}Y3ط­K'?4eQV#_هI_'[$_:Š(EPEPEPEPEPEPEPEPEPEPEPEP5[$u.G$GRgro7 ִ࿷M:I.b>X7, gnX뚝XZ}m)y~Xv,NN; ;5ƺڼ@$qkiDɝ |1Сg|KҳC.w S,6^˨Ǩjz!E*(K'瞂 T+YfIs#u?5ZD,L,!cQ7\¶?`@ (ĶuƎ^j70z6? ?$S~?Q@>-*ag|Ie- 1u8^?Щc#[Q?Х`#[Q?ТGEI?УoEoGB'B%:*O%K} ,tTK} <gX<gy-(Ry-([Q`#[Gޫ} ,TTKz([EoUKz(QR-УoUW;Ѭ,!U-RL$-o?*%Wy-Р[ [ [oUKz(Ҽ?qkKNm]򂴵kkۻa{H, /%Wy-Р[ [ [oUKz(l#l#⫩[Gޫ} 7O{-nsDdTW1G)ߏB~Kz+i)R>}S@|UEUܺ5 &MI㙀E 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^ 6A{EM^7zyCw uI%f&("7Efotoxx-15.11.1/data/images/resize.jpg0000664000175000017500000004153712616075370016053 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot0230#0100Fotoxx:paint_clone|tonemap|resize| 4http://ns.adobe.com/xap/1.0/ 245 373 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((f" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?mHA"mT*זD,?*?x ʢ|/Jem.݄b${ŽZFȤ8 cU2O^W_QI<WEb$E$3 TƮeV<ȌVKw 4dE?dK>QԐ*0fP?,6Q4uH՗?$k[.}!H?!T9pUJ}O6C(ͷ t5kWisݙ 7 <SHH a!]FzZ?ٶߛGmUٙ]:uONAF)1hbXM^ͷ ?-7?‹03uNR& `Yx;M_5s豘"~9f[~oٖߛE]}`imnӛIGNNAh OX5GQ ZhaEb1p=3]mQoYZiֺiG@w;dzrQ^&èD2G021WmoٖߛE]wi+G-i Wf<%ZEbdά0Kq?-7?}.3LN˸q ~3;ǿm?M,nb% i]Lh\æqϭw?ٶߛGmS Ǽ9=IcEWIFx&` GWI@ 3IE.h%4PQ@ 3IE-PEPEPEPE(SF(b9}gMngexf@X9D2jrխ ` 3]EҢm춷iN2c8Ʀv<7:,F84H[yRI6'ꨠ,ccCBh/R|8\@ K1@ E.(%M$kzSY)<"1n?!̓k!?>gC!##Vxf-2nLV֋|rG]wZѥ{hȊ28tWIE㬼NHZi0F !=^<.n./.-2ȸG+ ʻA=8Rbv3IJ)qI@Q@Q@Q@Q@ (j5r$'O6)mi7`:,Q8>еqt/Pk-lU8̌gω/d7[2hVYRrqM \{Q3\kqyK}*)0}Z<1=[G^隬pK^,b%1T nV?#O LHW^|Dsڟj}nyntmGI1PF fAj9{G$ec_y~ (ƾAUQJ$OdB[(i'ӭy|iK> 濍U ,P6-{DIՆgk4xќCs| (W'[;{ 12XmЄ. &k_Կ_"i4CU+ % 7"Ko?EszΥ373I{yay4M*($ d(:hSmA$<Ѧci!917% 7"Ko?EU~!մ{/ T8NU'CXγ.}$PiW[-+*Bʼnw:PG.~;Ay? )WE* #xAV?f )>Ebby~ (ſAW<<{D_ߔ⫱nPmyWqIހ3~gdC?{S/a2OͫuYuMD A8,I]Co?EAp5=&{7K9Kw!T #qwoOi6wVH,ڝ)?$Ov^(,>քDtdHe!Q@Q@Q@Q@ +;Z-4c+Em*r9(2ORQKqj˰csTgv-Z!llG ޸ɮ<iIt[qk*$Y.ё늷gJkko!-ρWsFhXZCbGzIchEta2 &gs.ɴ4yA1"5IY42WѨ=>M]sA, F*>Py"  S$N𝕕\_]A{cպpx2}J#)gڿtP?!Ce⩈Ko [D^4l-L|ӷFc{]:Rkɼ,G+f^`~TِơTfC?Qpr?ʞ_SAP?ed5 Z6d?lUِơT2Oͩ“,2gdQF+. żC$c?ṁ~5 kheuMm6,}mUs)?D4P|Z}3 B;̽$Fб8 ,rƙNQEQEQEQEXdsM1/5(c$Tb/|&i*ʬrGּJ;OczƓ4ņc=kf[g{xqy{RI嫘VB=itYCPA<8ux~GnG#jLeΠm{#dhW8>\bf$5%sԌZ/g_-H`.:J0#Iʌ~F00ޜɨ?~7֥?5kw^7U4M+DO򤸹tQ09i3Q_ MG ?tT[|Mgak0`"r9'#kRao Ft|]1<;HΩ=20]FbT>#6\rG=3^WY+M۷\8/~^y@yՂy4F Fd/r8**po4+D .mR>Gϡ ȱה?-i w7٬nȆ&JΰNQyP;xr:B8Z/@5 q IC84<1Q_F4??\Ҽ╲i"fr̉ MTM&afmmdvKyl$91Q1Q𿓦]r4byXpC($ 5$EMJQDY Ye?B$r?ΝfMGֵE=Aظ oFt??KΛG1 Od]iE7} YӾj8?O~!֚&M@'PU5m\玦6 &ݖ>Tf9+Į҈ 1{L0((((j8y/>u ,FQޤPmցC7eیD"1=*cBb..lmeC/ {Vz]"xYg9;G;GlfOi3r鶏NdZ B19$zjaIPHH-gn Ϣ j(ojc!H}E1}%ӚدH6.zUy4:W/6e#!'di_ d 6Mc-!((l%\,UqTҿadi_ d bv 8B&P:*M#J]'Q.(XյdeXM?bdi_ ?+v @{3yeO_Z42WO 국3Z2ܿ;dlc,{VoF@O#J]'Pmce,JFXϵQ`k]Oq8{>@ފ%SSF}:iatRT]ͥoE1䌑 Tv-vn%c@K󿳴_8mEq֥+i,!o(WS#J]'Q.)Ř4M lKIX;±V ʟqWwIy6Lȥ0jF@O#J]'Qpridؿ2#J^di_ d ܟ_΍#J]'Q.(UY>!ܸ׿TyR#$e8bdi_ ?+v @z}V1ComQҳC<3 ;8FbHTHIҙ.*Alm`=DHʀ%ciJCZ((((c5ާu^FϠ>hWbїÖFxk (F Ia骵f,gQ|1C~f.n'lc^B>e]EMԃ/ukDM_6J%# C|(yZH~+F7RF+2Ayz<!0ByNMz"bnځEfդ@YN% ;;'{}6lIq+H5EnΫWēYz\q^ݻF!9@^ :n+)`{`VSRcBYڠ{U ~e@Z,j޹+(,:(?6'#Ibt"))VUd` E.+3ݓYq^u:zEmku= `![Faz)1\nMbwslm$Zu6Hر9?x=xQ[P@l']cmmHo {qE}.v03HEW/×1i :3]@53\e.IRwG^x{ŗ4ڂ"VLs''u?;u}5\]~6T}r4a%ϻ:jjTݒ2OڭW7pEu,aO gmR1R$dnHau:+3]DRXc<}SҬTK*nS?/n:c5i4ځE1qI'(/Wt^E}B$!vag_B#SivZ<T@(laIQ=j X  4PbL8,  L..繅`K}xf,F0MZ~mΡZ&y"*O| ekmw*٭I$i6mw1$ڵ)(+K]ل a*c}1ZwBrTd?B1N W"r|s(̓M7W[YHi3:լњYAZ=ؐxG+Oֳ<7aeq5NX4/37\[915Y_W$gRSvҶњ`zmZh.b%U;{|z[Kh-rfwC. 6dl{2Gk/ Agm⻎k%IQCqԊs*RuųKqI7vZsM4VX5l.!Vv>ӮnoX<*^IT^&k %یXc1V ҅K.،15Zn^BDGCҦtf+ ;ɼ+ +_ߺM8ǀLȓRf4lv HDm!miqVFsSFEp1kq'WzR=Qv:zsZYm^xT`u}+O+wtWyl sӿ ;Q5PHM G.I,?Z}dH9ܨB9'oKwъ]Z/)YX`1#(j/?Py? 3m#7qYE]LgD v3r1 Qcl}OpNnJ%9{RG4I,2$ʺ=? Ba=F_*%$jʏg,k~sw')th+sέOTp}Q⇎&4ʔM`FTSSWEfwn蒳<ޙmu5SGR[ɠ; NŽ[`(((((((((((yklזJ  zwZc4ɨpS/=U,X}ڙ<8 E,nfhw"2%D(b;SiYV8!^á֢ӕͽ[8>g`9w 鲆l1&b  ٢3tL8 S%H'q$wl h { r pJ6 'ְ.-hYYOq[4P$}:/-m1"[OPEl.lY/-A;"z0knb@ġG`:Sh/uGQt ʎלfZܰ6J&WݾS^Fk5mN8I$9:.$WyvE9P{(v _6Xٽֶh&I[:<11K,^T2Z8O{fvKj-"H<9P3ڶ lRd"w2yeU'Q{UERPQK1@ E.(%RPQK1@ E.(%RPQK1@ E-qnz}mzףl?4f_^U?њfz6=WFhe|/_Omzףl?4f_^U?њfz6=WFhe|/_Omzףl?4f_^U?њfz6=WFhe|/_Omzףl?4f_^U?њfz6=W 1F+)bXTzZd%|W.U'ٵߧMU?lzרͫm??J-~i~z6=WqK0{kdYUpq?:[78c&Rh%|/_L6~Yt= njOjOOP%|/_QW*jOOP%|/_L>=U5xcHe%Dd pAg''֊SE%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ * r!qC58J٠ sx[׭tL1FiKFE돸i-mF} kD2ګ>b̥V3tK-`٦UjX8^𭩴o崳;9Ù#L㜌7''ށXӇVrKKj.V^n  95DKknF^a<;gVO &%\^%ٌ/^u|7%dG198})kot7dEl;I?4H4di%Oݴ?QqƳ` HeOVn|P.l`k{e{0UTV\wQxCjYJk-Je28tyVu0C D$r 89Xς̭{ ԫ~N }xz-Z5 OAYx;㶱k($x;!;-2YP`7oԽ\|zqRXh]\I+4 ϒ|2sPy:]ytn$ݧc+291#0 !5d\ಒ;d1XF*̀L[ 9r5=[?Z) mQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ *1-lU3@{x hњϿ̿QjanhcbI+5$'}h*DےHOFh;>>=G}ex´h K  ԣ N)p^Ow,y_ EU3@(fotoxx-15.11.1/data/images/upright.jpg0000644000175000017500000002341712616075370016227 0ustar micomicoJFIFtExifMM*JR(1Zgnome-screenshot 3http://ns.adobe.com/xap/1.0/ 92 331 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((\K" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?R8B$Ii&A Uge8,%G,]s=oʹ1E.%lCu EF0*{ʈ}{^S_M,@U[vϮ:2g |U,G'=ЮF8$QzRc%+/QRnVdS"^ [i*/Y@wNNz[a)aOIQ)`+ . m U6r$QX;Tu]}V[ %7~.67E=er:>qi\n&=6PO8Mm.w;TnWh<՗DVK3z<ă#CZ|C@KtZ` R^x݌c\,z&迕Uwv0}f+Jk5ƻ\`QլlgP綘,IP8֋_Cާ_Qz/^;ז:]e3_\:\-&z9\׵[oK2y~l-۟z/^CwE=ź1kg?oyp:mS8{VEěkŏAy-͏.n p: _סTnW xk'Wm0Z:c[TÅꎟ.]+Uen dp]xҋH=p_ʸ 3S5o-[IN=EokzY-F{wK yCƋǢnQz/U<<.-Qz/U<<.-Qz/U<<.-Qz/U<<.-Qz/U<<.-Qz/U<<.-Qz/U<<.-Qz/U<<.-Qz/U<<.-1V2+ȉ8Nү^Y?xhX8h<1\ዟYqe z(ӈy^<_wXVƲ6.xisCs嶺%kX'JZ>A?\?;קFX&|3q/~;xzz?tbKk O$! +P1 Xݚb4_.$ip+C<_wGy^0!=|/qsϪh]B+Dt]y^=c [[ Ge]1dn7qW\i:힬LX w;< g kz05_ҴWg?fǖx?G5mbZYY -:(0^[lڪ]ZD"F>c]y^=b!==o=zz,g 3ޏ3ަ?;ע.|z*\px`Q^;}fKv m{W!WW('w3ۜVKwm_me2~acjko{}/msmontwV9G[Ф5goiaMߍA/$x yyI':%C/G.Y%-z?~VoL54o MpΐBov sX:%}"\j-dl (E-o֖6kFDu|ӊձ.4WWMtӮ&*3u~/~W[%k:4NQnM+-}4[hmmɑJ䳉G*$Zmš4)t\cuwQl5R=AgqXbTo+8w6㰭'7(KuS F3lvZ#mn")YaC`zk7I]r_+;-2Xٶ7c'{+GI]r_+-q:nn=wiOtM*a,g'CK YH05+9{uSMh}zלiӴωpYq kF$85m[B:cڴ'ܺK~70֌)j{c'\(Hz9b?qY.%ED3+n~<ֶ$gS:xKx\G 츒(٢ A6$ j1n M]nv/,Js:c޽Zɴn3uCP9{ަR ֤Ʋo&F.Ӯ'7SF7U[8"e%w}B~#:!&"V#3{cCKs&9w Eyzh"Fo]HXT;f|]mrUa'Ѝ= rPY&[?ǻ9#<{}XĪI~MφIjfͻrsWM6t Nxt})i-YUHN5[%[馎Uo91<*X_q.*HGBk'S-ukJ'uL `dEh )pޕ^d>D]:,=&Ҍ7`uIou-FE5Cw ^>zQb]j׼_!;~{k FoSHHީoT\VZ>跫5OQuӹ r\D1r8`xUh R Gua:e\8XNioJivhs-E+b|qO˫;U[N`>s>_zQRU7*o̞6Si\ '6iFO$#1XcU#)Ul$M*g*E{s 0CnB3W]I%Ebٔw^FJú-|ŠXUC'4oJq|s;oq( z2*8OR==jl7!9Iȕ 975${k:FU96ϧޭ 5o&F7'Q_fyg~";HdV@?U{>-BKkUE%ͱRQjp%t+@WFr0Xzz]ti lHiYtk=5Jz^O5s"<RWEZptj:ym䗐}]WEYZt139%ʰ(\n*rf8"IR d,C)s{[ yeeHʼ[pFOw jĞ4b=6{Yovk4Yn?\oj u- wucGNzC8oJ0ޕΰBYvSQmVqۧQ[:mpޕSR4&u&S&ybp?P\ZU=KH:RՙG4QhD=UA*J( $$ O-'v?ZTTrGvfai? t ?:iQG${37 I]Q.+J9#=Jrm# TQtQTȗ)Kv0FE-(>}L*'&@?ZP&@?G&@?ZP[}:[ީSU(6E֥3tߕ ?4߅ Т34߅ u+EE6$(*ƛcr,cԼaV M/M/´( M/œFXt"%*08Q@ tYe<FAMiru> Egbi bi Egb +=&I_Vh(fotoxx-15.11.1/data/images/rename.jpg0000644000175000017500000002615012616075370016011 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot0230.0100Fotoxx:resize|tonemap| Fotoxx:tonemap|resize| 4http://ns.adobe.com/xap/1.0/ 189 375 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? +HEI*,J71q `Si5,ށŽIq>&)"C,uV|qapv! l;,"q@kj1DRX+׶:uwf9.Ceq:5֡Nɚ Hpc A~g0[x K&<ٹמAֵg8>H-&H Ϧ*+OkKsXwwSҴm5yo>߱i::Qqsq;T)wrwruҝǦ}$8/٭?¼I5;9=JO%$*x $N$~ohH;UyYT'+:N*"9$Ü|jӒ($mOQ -8Vʕ߳߱G+uWT"G=28[pg=z-Ju^!#Hc]&n:C~T3ɥZ[en@j"WoPx6aq& xoV[bbD7otCX;Ih n؁w/yΧg-Kkd' Sj(.-^< :6]abɵhئ4֋ɵhأɵhبG=h6y6=h_&}b&}b`Kϴ?Qϴ?T^`\ |_<_y֋/k>߱Gk>߱Qy֏0zp%m(m*/0zZ.MC~Ermb(=~"-}r3(ď%?Z-AUm58;єG[ߓ>N)0RqwF&ae)%ǫ6I?K*&)P kn4o4(NOS*1 $R{41真ɭ=֍杄f\ɤ3HO֞FEg.'?ϗ'kOyys7ΓM2H%$dZ lϗ'4ɭ=6M1|&e-WX8T\g4o4X.fғgiiW0A?Qu$j c*mj!m3>{xz`Z;ϩxaY,#c #lIǵ1j2Ȋ RѨN vee e)?ynmckC":C #iǘy^3Oq׷s.NlUYXt?Oֵ֧㴒(#tPųjF+Qa/WFbnwᚽO<8Bmܼ‹ғF+d5ԬPTV+1\}ŔȪ7))i) ((((()i)h#   KoK>m0%B0zhgz)zڊQQǽ&F3, ))zLSmg(nMAKް5T]BJ-t,jnARI\cl$bgLcW=pi;% 9sYK;X?tXHARY⵱B`8xS4iRYR>RW{ScTmWp,0űNu6{6]A-b[xAFijsV؈P_zR hvU'V6I)!d?Búbnp =MdgA}>ȭBa),F ERݠDXpÂ{EէlF T=rh.=wڶÕ)s)ato+GI_zV|PK'4 U4Zڡn3OPI#{9m8|/\U4.IYDS4͹PxtF֊瀥.-S+nonsZj1l }hm-0cG*>eksʏGRWg͋z/(b@*??Fx|ؿb6/还#Tjg͋z/(b@*??Fx|ؿB6/ЪHwW:DdG?/SGJ (5M ;;Wk|m0H7g׋+T?zN1[UrE|%²m8pj".|i}6&%]>dh{vZuսK tbfE6 snS1@On4n=Ӯ-u($)l8zg'nUGiA* <`wqhq@y5 #CmGZdX2#d >]ʉڍTSC0\< lֆmN}:xa(VT*P/K >o!rn$Il$v2}kL.<9IC,mp\g=p+sZ__NA3ַpzkm#_qR?ZlڍƚDqbFE?W_i>ӨN@cŤ^ xdy)g2sԌƉw,[A8?Ǝo!tdeўCQ 'e/oBilO$F?tQVXǩZdpjނщo m]Eq^e1x`:.$եRYfGPONjsuNn/4i''S_h|-(og2LwSHY.vYvxn#ft)'TE.uL'iTIFa9ᱚ݅խ,O2s{-OُGBG7|Qckm 4qF+;?G./1YV? ԇ:R[%jr+q\ڞCG h'%\t &xOnڱdqqVbx~9 xl6vX_[}R;-X/vcG?(-Gm-wNK7$'sUlMis%Tb)?7Go£<h\ؿk ɩ"XD?J?7Go£NϪS$ymAp̬@UJwow(ME1w%%$|vк?7GMT~Tb)<!s|S_ ߚ'o£=>XLTG,Wr(ٌ? TۤGrQOGLc3J<!O[ǕrO)QEQEQEQEQEQEQEQEQEQEQEQEg%_\pĮ[Hol\ KȠ=]=);g9\(.)#%dW6>Z-$a\`ym0@Jn];˘(:64XHnbVb8XMz1i2N0mܧtӛ<icV=V 0bu@ [bI P;=F$2+htu,PN+͜zDzv"i}693+f\+F2F_Oz%3g4/_ ɋ^Q4]fE#D+ۿn&hpXpxVVzt!v $ $t?w:[{O v L޴͓VXm%9n@,y=k\jӋZ0`PjhO6m]3pH~ьM1AQqI]TӶ+S떏D6U@VU{<-⨸ΣbJ煷U^ o.Q1\%Wo*J煷U_m|G%Wo*bW/ U[m|E1F+x[-Q U[uU{<-x[-Qp:Q*TU{<-⨸F(r^ o?*T\bJ煷U^ o.Q1\%Wo*J煷U_m|G%Wo*b*TU{<-⨸G¹J煷U^ o.QaF>U{<-x[-Qp:P8_m|G%Wo*w&\%Wo*J煷U=Gcm|G%Wo* *TU}<-⨸^(T-Ec_kha msE+fotoxx-15.11.1/data/images/edit-translation.jpg0000644000175000017500000003453512616075370020031 0ustar micomicoJFIFtExifMM*JR(1Zgnome-screenshot6Photoshop 3.08BIMresize sharpen 4http://ns.adobe.com/xap/1.0/ 416 352 C   (1#%(:3=<9387@H\N@DWE78PmQW_bghg>Mqypdx\egcC//cB8BccccccccccccccccccccccccccccccccccccccccccccccccccX#" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?U;ņ~af#C (AGU* ݞ:YN|ԩ+$-%=B7݁ObiH*9'ֵeUV' WSE9Z~؜,d #AK?*(% l\p㑓ة` 4Uk㛷?OE!ؼ+H>A1dO"?nƤ{Xɒ}MN /bȏ"?m Mݔ9'$}Mȏ"?s8" <~9ǥ7ȄC䈓=SoitQGJs)mm?--Tm@ ZKͷ6A֯ED3LQ>Q)[[š6nTkSȏ"?,20[ĸm y0w=6ߝitQGEao?/VEo.TkSȏ"?,(QW(#`BGGE (DytQ`BGGE (DytQ`BGGE (DytQ`BGGE (DytQ`B2Eegz㖍r?ƋU|7j8(2r[PGje#@` >!/% װ5*XE0vg V#F>Lޛ&[: CWh2Vky'ڴ۲, 3c;jezduZ]]0:G)C) W8>V-.T68$}ހ)˩O-ո6ݘL~2:VPʇ)(`)bls;I%Xl oc8KPy@,J4s*]\,8wgzctLieılۉyrȪ+d~xWQKu;dFe`ЃsJ4qi^R:?J}cЀLAOq@Y kh yy!kHȞT̠>{Փ IZO$P<@ g#hTϴ`qˊYVYZ I 3nDo*&s ۳Me1HZDx6su[]=R>Xl->fZq4ڲEp$RQ9:Ƶ]o:ĒFǮOJ_ pLH -0X /ԋ=2|wg/,% =ÃrWCZ;{zaHg4K~b܃NQ,f& 9iͤ[u(r2=MOif3d`@Ilf0$=2qb((((H}W?t*:~RcF]}7(uEIEo"8-B3 $lRqNDp$+#ѳr5z+H$)^<9G4Z8Z356@Fp"JSʶdFgs@5<ǩy2ʨZ6>f}ƒI[۱#yj.n;N}p~qYkvC#3]ۜgnׇY#hQP;xkqwswrH` _z֐a(ܮW#ЀptP HѮ#o~z .I[,/U`X秡k';^WtnXg2n;ʄySt9SI&C] sQi +?;@}pUg8CTSE֚"$$ewvSkTʡy8fN[oo(<ʑiWT3q}Cm&$2Ų AĖ004d)~ZbdU#,q!qWm㴆ᔔrGJujT xۇh0jҼB%P9=|:}jT'# 9$FčÁViT m9dm "";$Y%w 1#>Y/I]ǽdV8<`:q}ymg,/-â"6rZO"TbFbGI,,a=g}b9$źD` fGFEXeF H^ Y%@OԳrNO,0vňrrh`WH#YsqɧEQEQEQEAyΫ~bH}W?t)1.>E_~=p*hʤ (eA*[`XP1(S$8 ).{Oҟ@Ŗ7b"C<[$q"!#%iY7C z\C%β)ǭIE"%X0y((((((((((( :"遏^ji ſuE(%ru%D nVQX7C銎YX,eid$tgoPWc[ h*#֭wWD̞9wEuTFzpysWd/Fu T6ShۜZA,$ F;sU<8w6oI;\bQcm tԚ5'WH,EH=hł1#y-Qa\Bemۤ,O+r>D2x# +rH'px#,Hiqw'*t*}*W\[ΨHAGQ)mG#W~} -і0@gg?RrILUs0ϡdc9eV8 jDuXQEQEQEQEQEQEQEQEQEQEu?_QKޥ){}GE_uEHn4uujp<ۇy屔AbեP,؜޷/*,浫o]t>L m'5r[V؞rλX #Zkn pD(Ph.;8IUJJmĞzuMnc\A8أ#ӡ*(WP}۳}.ʕv$g@T6Ҽ$F"$e@?Z ( ( ( ( ( ( ( ( ( ( (#){Գ}E/zC1o?(yەo ?j9tw{{xD0pB$\wV+ml@)qNV{d,BP@=Oҩڝs4L]4nJ pϧZ%Q,9!zޭy& m$Ul`sׂR D6kcq.g!zL -; Fs,Mi|wRjc Cg<˥> '(d2o!` ~4bZb3Uё oL8o:ʚQ.'x.@>hFXȵ. p @KJWp1Tmy+.1,p$M?ñ}9O.0m7݁ r:?iQTV{tŖ8c ' ݳVm7*.NB(((((((((((:=?JHf-}WAQ}GE#5R)s8b1dMI[XZi~ΈwڭCy"u&2͓9'@j2AGzZ5 %QEQEQEQEQEQEQEQEQEQEG?ORg:^buE_QR3~=p*hʤ$k8I!yH9=PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPASqHqq?ԏsOғ2_QE}7*FkQG=?א): K%^_uy1 OZk f9]OI']Kb3 ю̤r>hRkKysCd+4k}^6!vQ ܽ1ֱ5e)sunL].޸ coR8}i.Nlxo^w9٠ 'ԭ⹊dI&y2+ɐNHڮV(V!;Ǹ3n!q@+jԥ`Oܫ3e{AImJ_\}EOs{Y5PrEf|RMFzkr9ݽL/hǔ,>@zgҳ>r5ΞQUo-pbzTsWfGFdx#2rvuٛf7DCI֤.aYP29U}>bJ[FjFz Z( ( ( ( ( ( ( ( ( ( ( R?_U?JyΫ~їM"/_QR3Hꖶ2g 7^:b ds* $l)N@,gq ''ri2(+EO<)5hZm-ehwܛwm.z@I ٶ_1R> 73["w,@ N8QiI´q`!<:Ie%&S06}uR&o%ۧF p'iOq!UUu`r1cZ[y.o>o0`tTc 5 {FO&#&ln3ZӪ{u`T* EU%2ĩJ_͏OcH,!*0{PLј1Csڥ((((((((((H}W?t*:~RcF]}7(uEHoJ=p*(((((((((((((((( #u\GIw(>E#-˨\$PA L"9|ڕyBZc(]O ƮEg}#Mh2$`Gb3VeKR0ElȎ]$:qekQDл.+P_f!cXawӟdOa ݋Ddc83ٕDvm1d9CH?mCڛ?7qf: B VU`OLU+JIHVb"c cYxGXzPy{ϗFO#^QSx.g/1wQ4(c cq:\O(˵o2m!q!˹,HrNx SG, 6PF3 3[d$)r g)eL?5$йQ{?Y%pw=KEQEQEQEQEQEQEQEQEQEQE:~U#u\ƌoWAQM"uxq`Ǹ oktR+ƁǮFr5141;D/%FxR[BLcLcޕher]SGۼhddCSuqf)lΩD #bd`G7ps@?e"/;zl>Bgܻ v슨20wp?Ɵg%b-P mSj[, m** $h"s1' rKL1f#,Ix¯d%цXd(|]>RM<䕤by=~WWF*O5z;8wyV&ᵶG%qio 20IkF6Oocb`1Dz+QFONıG4f9cIVPGM-a<^S6 ZYA?oj-o2y&ON2kI- HcQvm\zSU`eBJ f^jv"R YC99빆jI~Ŗn^!$5PF̏~њoR6> a@+A 3rAkpp0)@U=r{~&5)q;  z}kM^o9airݑӚViot?͵2۴E&0Wm %k$ eۥN mH6eŽ G&(((((((/?ԏsOҬ^&4eoWAT޷`-M֫Żɏqz|$pp~DQ'€&=h=j?3| Z7ZO?~g(m֍֡?G蟙 pp~DQ'€&=h=j?3| Z7ZO?~g(m֍֡?G蟙 pp~DQ'€&=h=j?3| Z7ZO?~g(m֍֡?G蟙 pp~DQ'€&=h=j?3| Z7ZO?~g(m֍֡?G蟙 t$#9Um7˰zT?~g(?Hf&QTڇD/!QB ((((((((((3KMo~Uo._}(rIrzT YdBp SQ_j& lF$e$gUʪlw]#sɦ#Gs7(89H&vB:5n# 20zerOUdK{dw8PdO dI"T%>ҐrOF_}jQ-+=@Ԣ1&$&xWc`zL ֥V$Ff6^{t2?*:O̒}>~QF~QPQ_OC?SQE1Q@Q@Q@Q@Q@Q@Q@Q@Q@5S("]nK';dzˎFEWK#md1?6px5E,^?1GG C2 y#C"ŀ@9Ֆ9 #BlU_>SNQ=/;WA SOsZ [*8b3SP\D:ҪiK23KCs?)sj;dG#e_w) ϒ8yywq׾)amX?/;}iFtvA[Zh~FZV//}ibbRO$SL ]G??G??V+D14`?0hEB=i{G} (0hG`B(L?Q?Т>}(=i{G} (0hG`B(L?Q?Т>}(=i{G} (0hG`B(L?Q?Т>}(=i{G} (0hG`B(L?Q?Т>}(=i{G} ( kWAaETfotoxx-15.11.1/data/images/panorama1.jpg0000644000175000017500000003633412616075370016426 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|tonemap| 4http://ns.adobe.com/xap/1.0/ 286 265 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?#@GNN:r_[Y}E4-]^+ծ@ܥI?oJW#mEh]AJ K*1%-lqҷZHO*ݦ$e?*1jNYlJnF(2m=ɪwB8nI /.L69p Z\ƀdIzӷAVe ]DC@k!( fgp&?*|[zzd/mm$ndĎAk>"X%6nvMpoꍪ 1EH.ެƴoSiۻE=m1j.:#]Ƞe9g~U皖yq֚s[W4s^2leS1izʍ~UmbVBY5̈́D5mNkSBկu\Ydphonpqkwu߉VS(QĢ5 )\iζb6`pY_ۓ ?*7AW!xOԙmERBGZRk KfYC9  ?*7AWxRZIctݝԂ8[]"O|mkh\,m4nZl8НMzʍ~U~hEOzʍ~Uz7p'=F?*`C*} o fw d⊕?Z*9PpyV%ׇRY>RR!bȋy'J3V:Ӥc#NMbyzgaCZD_Ry1N0QVDJnnܢ]‰iP ")|瘪731" jo21IC< [M3ۂ#``Rko=Lm^;֏y1JsMLP9p+(b; \S{K!|erGNzևy1Ns|;IMw!yYrNi5  mmu8w(vJ|瘤"bSIpXr}Tj<ق$+-0'8iy1GX.bͣK ݎJ&tۏyJ/UC<D?Q`u&&Ree W@85j}KW3ݦYHO5_z켈瘣ȋy,0M2P7B9`K&KhV*.q[^D_R1JrGV瘤!bʾeeZ!b"(\QU"("bʾeeZ"b"(\{1Ek\֯KOถ~< ִ)K+mIgݱ"Lbqڨ1K)%i$Q/$cqVn)7k){o$*`=y҆ϋOvE7vP-k*DB튅| o#F9ǭ[wK+^0dn#VW~h5nw"b>䫴§k yw ,xhSb^aERW1tӗQm\I>\2BIt ݕE 4[mn@|$4œY\=[ o2O侕&v96=m:r7o9\Aq4 $Z&=PE1ߋKI٭>h]$ 3L5h_m.8Hv c}JIr>k/9(}I{Ofb> U]ڤQI3٘\OrGV9|Oi[X6ܵXuErA-pzi-apum6ӴoT'7 WKcoqڢI>B0V{R4Zfa8_}0EYxZH$d{wooQH;;-$6^F+16\b8튩 ]m-4J72ugEҭoMw dU瞴4uTQ@M?f1c&)b V9 q >P9kODu]SPEgԐewyzϢW7lo0r:4+!Mǩs:˻+K+7Eu1&Wk7:m2vZ[q@I8v7Z.{t.!q0玟ZdѮnZm61r9$t4K05ojvmoc{KĬc[ӯauimaZN:쐻ag:Ff$m;{o{Jeń2O|9 eq2Mr+;\Ӫ{;[GXJzQ@Q@V =$ڹ@7#'EYXME%'ШveGJV* &KIMflJNX2۲Cl}*9&OFYc+:k֒ߏA?`W-#5w*H*9W޹2ɷfeLqM붺6hHcXJQQr?ϭiFrt0Ӆ+]Fg?o?iA:M_6!W*8yO9#y}/GsF_&ZJh6RDveqZy-ooz'=  hz4As2VEZ_:gcGp9PsS:A1j3G* nK³i}on2Q`ckJb &ԛђ`\ψ749}ΰsk5@ck)n#aQ($qފ,r}f'?*x݁Mka)vJrQ^o AnJ@l-/t =B6μ7 8<@\쨥4QEQEy#O5ԞBlFZfEAԹi1cp86coP?IX(T^$\*ȊDAB_:.v#xzQrnG |}wVmo$r rJd21>7y0WQ_N)?y0Zja?QxPy0Gj(9}=M5A3$RGF`C) ^L??Qӧ]ݽGBGf-;qs^&Fj!FzZ_Fl廵7X;  , +;p,'GW'9ϸsT{D7Q*Dé=qyx^O9:,$F+CF𝵍p\ DjB.V:Yk(xyYHXT[')DLuQHaEPEPMXMȨqN1&lUF? R !2`zzR=1Tl"UA@(JjmvKK?I>/FoZ_-v:cڅw|IM=VډeDsG+|T-<_=Ѵowdgq<:1ץu8sQ `i&߰ySa{8Zn8ݎml+e_O\( rUʎ:W7גȖvƕ\~\sHg]EqƚӮ]m- حY?|7֬k힣-EY}F,Oxu4U].KI }3VQE!Q@Q@Q@Q@Q@Q@Q@14{K +A }04H Ɍ ϼC?L jrAfbGL_*6ʼͩ4]0.߱G9{ZCi'PχEy>U;mGyNP{+OԞ7I,fFz$,A&GnbSJ^ӿԊ4r.\AS} I+X"OaiN[y>P{>RX'8  a ɗjёOen[_zZ6_RH!keBr9s[TPQ(8" ¨:(((((((((?败dCأ䴯ꗺ͵gʓ] J,j$qZ7.5%L$sYr?*d~ⷊ8Y!7I0+13Ju,loqr洢n{^_}_^GuhEg9|pww|c4tup\Xcg!zkx>m.A'8cnV[;Y X{K2<"PFr0*C*4:TLdN;ԣ\u{X6J-BFCg'4>-:Y*ei1^ P196\@QQ~t𔥔p._B"Le >R,-M/Nߔ~QW%𵔷Lg 5^qFҘjE uTfd=\U;.5ՂPHu9MhMK)Sqr=ػ+:m,~w Wr54z:@DUfAsiR@K ЀzSYjvNx{ nl湻YgFeZ|7cjJd5@Mբ) (((((*v(gy\wf̏:϶?*??ܟM\߹?,Qc^[hdFstZ+}j.iB@X~iS?:jv3o/e;@pxL\z?r5<)ݳWvLg'7qnkMxr}GR[8]2HV>{%< vgv>nLr{c1ֹaFf/Z #9=@tC8gݽ2ޟytV :]O_QPkxXaX1oܸ+Wu3Lb? 5ֽ4³^[i)<9[n9OOֻLњc@uMaCks:=F d_xJu* t{ˈ$TqWcE!5DŽ+Z%lTO{}+w{/N(XEsE, Gs>,X)VXբ*YI$TjS%-s6J]wykۡ֌ӸrjQo%ϙ3$rTܧjψ4 umrC=6FB>] X}nqOt4SM gU<4ib&ӭIM)Sc]h_ZOcX]' ?ZEۻQEQEQEȢKIe?>h@'-(;|ϦK O?3^{Kcch6ѝAs{b;M_YͫIXKTU 2A 贫M=)U2'G`OGSz^R:gyF3^iϺ ?I?Jn,uA >cg׭A-OL,}M-y kf=.?i0F zXPSjŢ) *N #bxPQZr?/?¬<VӴP5+SV>6 Wgedd 7\U6ibxQ?(gO 7;i?QbZ_اޟiKppTw-LFۭ?/QOzPxc@FG5zƒxGʍ[4s@Uit?*8hZh6֒iMmoY 0d9{fZokm&hE;`&/\:G.?3A|KquuO/6Lesy[rʵtO|ݥL~Զ"F]݋0^6đ-QHaEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPfotoxx-15.11.1/data/images/gammachart-reduced.png0000644000175000017500000003777012616075370020275 0ustar micomicoPNG  IHDRYA IDATx^y`lB. !`;A!RЪ`џYwR[O$p$#fsmlc3vf<3<ϼWUtt4K4i)FC pڵ W|_ŋ}tۅl&oۡCV7_\bƍ=n Z.11Wh#yyy#F򊋋{[[[32N>'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-15.11.1/data/images/albums2.jpg0000644000175000017500000003130712616075370016107 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:tonemap|resize| 4http://ns.adobe.com/xap/1.0/ 199 360 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((<" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Rc%TU#U"}`*;&;Gy}@f{);3g{W#m=,*RoD{71́*ϗH*p{{ĕUZn#e[H 0:1m5 k͉UtCI&V :\HgFk=&eU 끞q۽gjWEegl`*n`SZgV%JaYpi`_Vy:73ȸRBA,Fă=hXz/Iz/+ɴx%յ_]%cE<;޴n|KǬT26N^_6xڗ6H=o x\X˱[#sݔpڰ?w-%hZ ^@9qX !F运2/5Dh 0VB'Z}f#٘o2W88Qpo 7EyN=k}cqc5) a ]7mGAQ\k,I[ajM'X%8 \. $ ^yqq!G(FͩbjQws]j \/rzЌyqq!V77-Y miPջ\_SDbsw5'Z\_Qs|;Wk:m,XWǞq3qq!GrB%mB6LpG%t;K{-tBL$8ɮˏ ӿ4aiF9#=' N7k[mh`̟-;xG<omm=ϻ2Zw?ƵѶHk>ӿ4aiF9#=' N7hZhG${vbصQoH m*qU1Hp>8c[i8隗y!J*C&NZ\iqtVZtщ!)#= #scM@4s8Ey(:j|5660Fb!+vZ^f/_e>-sP%.N&n!6y$U=g]vj i̫y0>V}+,{OmhͲ k{J״xf#$saG#%Q)R{yK\ڢ=w4}.zXQDZdPNqF?JcVOԊqKmѸihw3IE.7SMѸhC0t =TJ?*ZZM\i܆kkkⲺYnd's ;XOtњ5;ǰ$kGϵѯp{Oti']ߺnh ǰ$kGϵѯp{Oti']ߺnh ǰ$kGϵѯp{Oti']ߺnh ǰ$kGϵѯp{Oti/kkM^x!_}1DcPNH'EdEqZ_XQNֆh1_}~ _}~ hf;_߃GZ4R͖yg&2*\ ժ\PEPTVD翾rD9%`5vϋvR^'K%M( ?` ά F T$9[j&zk4=V[࿰rJ8Op}0j9u?쥴*K UF5ŅRS哺jG҅>xEŧm^gk6Jټs\]cr ΗshÓ ޟ޹촷R( $$ obk\H|KS/++t_xSI.]: Huu#\sPm k;iڇ٬.>ڎ>'J.a_c}JOukdW Q[?ZڼN M#QCE.$b Oғ-ޥ}}4ƏlۃbY/OsI-QM</5[/̖ٙ$AV<=#^m$1~#mwh#6i}$1 ?,~5@|Npڼe<}dlG 慨hsڷtB;k]A!wH؃0n|mS:cXu ňKܙ|Fu/loo!Mup{=ti7Y7 j&,BүT *{=ŭoC"AAՈ tXWzT.d%p}Vvxeҵt[k /9]pGgPmoԷ1—$Pp:zW%iךdjiK45t#AԌZV K.!UcoÓiQ_Cq]]C+ # z`@>q\F[7 CYC~R07X~U\zVڎ8e#dd'Kk_֣zOeo˨ mJc⵴_i:Eee\{ю_@Fhri^e%k$AFV8]:smsisMu%L_pѡ`28'뚚sǮxo a1Oc O=P:)M%QEQEQEQEQElE6'&zdq*Z $'ۏ\yZkF$i1GL ~Uok,mm1!*A\xwfjԣԛ~W\aG8qj1݋ɩ!q ]C\@ym4J.Dn<ۍïօ]ڻ"e6qk< fӴdFK1$ H8Z%8"n8 v( ٽkg6qR~ /4{n$m6)@ać$utQpxsy=<{P fݻN6W)+^܁{mol S޻(Xmĺ+$ j0# rb7UoĖVPi h3KsBOr>|+(;;WV8Om_T5ɺ0`1l֞kmRPӯBrA oX^kTnPɎ7tx(-uK+}V=6& V3'VҺ(3iҴhycL]1|A -1,[4`g 7c=3On#wo&Rk[MJxF0΃O v/X\ww{/qq67;c[TsGe+8Q/nXZu/"rKlOZci)(O]_KHԖٮof8$z4/j!)H͖9&_F%>^3FhZL)|M"a q9Vtŧx|GtYh+F>gO@]m% i( ( ( ( ( ( 찿gUpN?j6Xh8姗j6V.#s vX$¦Kٳ'7yK i aaGMտuFk{M~WZǕǠ;'#mkٍݛxNO( [9;w:[A-o>Ռ|1 E(WPW{^Օgk7zJmMzwXhiVFD+kqo"/#y.-yE|E}qXj{sml NsqW {Q8[Y;Yˎ 699I[ku&z҈1 `*m=)&/>!񥶒dm~yXqϭbxPk>֚v.l@5qE.ӕ6>潴 .AcӜRZZֿki3ԢԭH>̤-Jľ'.E<=ɹKXTb 99kM%C[%;]}O[F<ʋ]G>&96*jF!YCɸgǽ]c,Ludd&5=kMxyO,r{Rkv'h!/lPeN}GIףmŌBY ^?kXo*+QN7"Dcrvf(& }ylHUM#O1bD BM%<_Q>%U^J I|\h"KxoV쮱[ ijCm #~5N'kxZaC,?c,5KF "u6j1$cnz <k]֫r/$O$Zq,QNf ר#MX[zeF HlEPEPEPEPEPEPEPEPKkG WM=_@lo 67?bC(!QfcWPF)z?=_@lo 67?bC(!QfcWPF)z?=_@͍=_FS67z?€1L!lo ~(3cWQC(͍=_FS67z?€1L!lo ~(3cWQC(͍=_FS67z?€IM!J( ( ( ( ( ( ( ( ( ( ( ( QIJ($Y..de1$j *bO׊=N/o?׉\Wv5 HO0 I' { s(?(MCoܐrjXwSO&6m1/qsǥzP m᨞FQi "4(I +׭tzQǥQ$ȣK=jFi(fotoxx-15.11.1/data/images/print.jpg0000644000175000017500000006510712616075370015703 0ustar micomicoJFIFHH ExifMM*JR(iZHH0230h0100Fotoxx:trim|resize|trim| Fotoxx:trim|resize|trim|resize|tonemap| Fotoxx:tonemap| Fotoxx:resize|tonemap| bhttp://ns.adobe.com/xap/1.0/ 1 2 3 0 8 251 850 1 2 2 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?uR-y4VH"H*;CN3ޤ umFtI)-[l<赮WƗpjb( B=-VS,nmFk{#gq~u"v20&el_ ⻨yJNdi{9X^`\4[;m$GbnFKi.3!8"4sZ E]^cmc8.dw}>R =[_ޜHW\^mH-NHŬ,CȲa2B7QOW:&@BjHG#^T9=i\,tuĎ*wTѴuw]ul(̓+FdW6<3\YBEyg?O3ޏ3ހ#֣oZ==?j>?O3ޏ3ހ#֣oZ==?j>?O3ޏ3ހ#֣oZ==?j>?O3ޏ3ހ#֣oZ==?j>{'Fz ?׿֣ ?֩7ѼP+??}??&F@X ?֣ ?֩77`O`OIQP#M‱8ϽZkͶ56[i ٪}mڽT}݌w7zy8XF#ΖVMN~3LX*L|ir0mkuKX]J;I)`J$}+*W7G;HPe;&RT뽙X ;4HьȜfG=".%k7Fy[?;\T$jO"O:xy}[.N1r1;[жŨMl(H6=yiEg3Z- y5Kɶ[$W VH1$!ڥ5MoUVn"i0a`_`}&}jxx,GQTR9'nRYëSݾ:ӻtIHQ}rqsޛ{f"+i~8R??DQ8FMz~4hKun5`It7'ZgրHƅ{{;^3P_R$)C[Yo7h~fd٬}tAw}=1Z[77fsM95ߝ1Q{K !|Ђ:jr?Ю<9Py:?7?Ɨu\IGh=L?7?ƍ?7?Ƶ7G52w\Ϝߐ7\Ϝߐߝ?ᩓ|Ѻ|ַߝ L?7?ƍ?7?Ƶ7Gjd9!4n9!5y:>7ES'uuoy:.;ook|oΌ?ᩓ|Ѻ|ֶч~t\52w\Ϝߐ7\Ϝߐy:0o΋NFZ7Fps>s~Chs>s~Ck[y:.;ookbO7F%踮d9!4n9!5;~t|o΋S$5Gߐ}HڽzV-I͙?eBg=mKI xe>5 Uq%4w?jj`p?>슳vK{]_b-1jZo2.vo1M$&Ӧmi6->h`t~T`zʀ(DˌWieYl.2ƌτ8kOh]td漡|B@' w@JƨI 1+D&E_)KsP^뺼QB$PIcщ1\?kc??ƏUoټ||ZӮԴKF16HUAP3sy@53y@5377_v:\Zx\e-'d;D#Pnj4_q/.ⷆI[[y&,|GiQQk:١U&yUA@\dRio??Fo??ZJ E/ Qq`zʀћ֖!||i`zʔ?*hJ wjMoQ:Foga" K0qY\&3Jnp I!Ҁ.tz}.5}@;Zj |J8VƉ{_=[D!X_,[8+ћ֖!s}Xﴙe}g^??oMiU=?>o:/{=1 s-R6r<(=Q%}(Gs ,| A<|ӆ̃hFO0\B۶M!\Ϋci%MrNc Jr֎{ wV{,ܩtРI& _J,GbO9i\K8/MG>c$HD΁WbRMnE֫m|lq: zڞKo*"$/>Y:SR;M;yp{L:p[<55ȭt^vA(x|18cSjQuI8Opmʹ $}MZq?$aT7ۣP_['8ENg?OhqA)Jָ_F=alBy"p[q'x_mj p[ُ`=Oy ?Ə=?}?Ƽ2,5}Nx-S#+nna$ZdM]k$so(Yv0 `iX.MmN^ԟI)eSP`#/]ԡ{VPcd1͜gv9CEvC9?&_MrLR}/}7y2oe}cO h真/ԻEMrLG><ƥF,_oe j]o8_El]W2 csMXo ׬U.? ^Тl'_,??U_lakO* \s(PHNG9qKGL4ؔTV1&pR̸qH<qRGl<OJ]TuB~ô*p[}k2/ Zj?bKr<˓8.t[Ҫ\:nYG*U+#Pȷzd$j\TUqÖ..ExٷJ; YBGW|2i[{Df xQX}tVb]IK?7 [rO{ E# (t&kli,R\HW v.2T[B䙜n+[y Jʐ,+&,VcIֆ#äJH -*U5Shs23FJc*H{V^;Ƹ , 8<E;P4t'RY pxZv e,Q#jJ1N:4>k"\mxfHQ< AҦ%. :VLު׺^c,,Udsa;xcOTh]0÷w{0l&]YeH0ߥf ~3[U*hWA57 2\~IN--Qgca0<=r1򞸮Zhۻm٣*H<,vOiaKLKI(dHvn`:>Uʷ 4폏h COѠ.#R6M).rpփlGe%X##֚"T]m0 1`zp5-jVq}2^7Q`5,ݭ와dBv'I|7c&I83$I$Em . uԇ ڪ-׃d_AT &:$QU-moʹ#֟s>d8{4;K\.@J'~3۝&  &9sVcK̓:4jG4@,{\ol^5lqDd’xczW+MI"?!T\FF=JZ#(;P9MGX ӅԛF r5 [^bKQ8/oZHlgb0\9iE˨![Z7 ůɜ>GdUѸw-}QJ{X,% ^JbĝZ7ųu!9Ax4=#j֍0-^ONNyTǚǭUѸw,˱'qZqwqL,Z.:H>]J?\󚩸wqH,Z\ZL/Z7xx7n?i>FPn?hǭǭAwq@o֍OzƬ{OB9ˏj^Т̽fI#AԱLYxz{mQn"=֮XSĘzQEY@!Y#KDn@ꊪrINO{ӼK ۩-Eb՝>İfP@&V8K+!D=q+su(g%*K28X=/Kr,NT'1TtduXFqkHǕY _]\51J{ٷ!O(Ãq)y9,DKۀj+v zNAcAs'"E ҭ* `;U?ZEi$N&G ?Vo|c'5.>tqzsmG_η [{ YAY7o:}uAS>撉JJk[#}#r9:c5z=>HlZYf@?xorkulӄ3I =;.ǟư|s$Zܟ+6WXzl,pp{;*lUUUeUAU2lkǦ.c1';qj7EEG7lU7ğ@hBj(eBGm{ Mί;w-$p`gRq~v}M(lUn^ɮZUjG>ʥv15Tn X°MÑULԇ6Oj[g$WEEܠ3x,r}(SgNhkzńVPYգHd!U%Sï)~H% 42/.4ynJ r9o9e[Gi1<c=jSokka DB3vĜmOL{ j&s p \Di|'Ș;OR^iv$r^YCpgasRtz+s%Vp_v $j-wUԧK iW6!g=1֝ƅ]$ҭp!641-R0H#?b #[@ яrA}E68R,qUQҜ9 w'Xvډ"'5?-xssu-ķ7 ynv4 lǩ ~5ssgsBq1¸j3yfo?W8ҁF^/oҧLt4}N4f%yY7P2 h<,Iq8 y˻R.ws ^r1w-Xg{iZXV.w^ .f6Gd3kG,>n"rcnT#T!4ZSfY4RFs(;kz$Hⱒo#*C&Kgxv-xOo1 9$#I Ҝ4R/g\Sc[XL $NJc>y{oj3Z:cu=sڗSu^yP6d#8br9ӥ^?[ZkbB6~)x%:TGl;R kj7ݍåv @p=1tҢkﲴ9?n 3BhO,7^ӊB!/48-䎝 V+z>Uؤ-y BG<񊿢jz47Vyc*NMC tVEƣ6)x61D"X,hQT>ۧNnA;?֠e;Y*5K@C9[STKFjmaB7WHm?vIIpqVB73MP%kT5(TضŒl. kAki4VKӮōX<ΡII%U zrZ!\L]L٧`3LLe Io(Č\g<#Ҵm,-' yck;ձOP?:nZ9dQnSsҋbrګǂ"R:bM/N4K gT$hO9Z8 (ԓjG4Ww $X39UF1ESd^",oز&v)pxp]j$YUW-zcBVM"՛y *[wOH60\jIn"gp@ޥ2Nq4˶-"f0S -U29ǫ 9#!iP[!9'?ݨ,{$TuZ8n棆+ Q!֓Bdvȍq$C }ۂ3S5]6 _O{+7ec;J$,ʅ 穤H[$Rg8;cO[%K=)R%e rACsҁܦt36';<}qRXG̭v,V$PZڇ=0-i@⍦)p=)(QE((((((=(o{\A4Q  |urΡ_MGW)QE1Q@QHE#p28u8J..Y.wvKvq,B+g(R d`#.72px^yܽcQ-n{qfm3d:Zv2M'"MU6,\̇o'z,;~T0FD2 z\:X;Fj*E$U#ؓ7UhŸIq:"2X.wSQʢC-2XԊ}C(ȦHo?QMFE6vE(o?Sho?QMFE6vE(o?ShWo ׬U?z^fТ-g-X-|gŪXHC#q WedCq"({4ALT֯agg 3DBwgvG9~n`ծm ]YBӨf_w͎{~uw'm^̊ Be]<W_^Q)~m?QtcuytǹTuܰ,i c&qWd q3$'.mFzx7'0EcuTƩ{ o}By@||՛sj+ ($ s)VByܛ\Q~Cm} ZLH̟£1q^RceŸe}O'$RH[{熘1e/38Bx< ^Oc( Wޡ,un$i+!rU7O,Z\Ƒc{tu!KqһAyj8u?*_dG?ҘX£ۮ]|`Z\"ʆ) l'n}'*,v9揶_E.su\q_mfቔ!,]iS[Q#k}cRm&"qI83G;v[o&1s -Hnn77VTO"%OL@/n'Iԟl.bs2->IE7\hWߜC)MK~q}q4|Q'֗[M4q0Xþ3cF}_jq ӷ_k>?ցXQ \.ZdEWq8psX kv7al@C~}mqw5ʬV',+r]r.bG?΀%]_T_k:>k?1~t-k?1~tl@Q@=.?*vG/΀%m}QO =*Z_/?/΀AEEwso.x#MP#U;q?-%GKm Zuqt!";FI$IEdA[1vO Y7zTvkVdPF 31ZmLYK60~MBoqG5 hu73C;cNHzPآ.M HĮ G 1woL:PFn1{+x=+&?hzHN< r3kXO恍1!pT1'QYKC*;U{|嶲f;QIN:OƩ<4j #tt A59-aIUOnO$⹍&PJ#5xWú%Ωo[I$neQ}q [~uzr]*j HN%L# +5ӴF Qp E+"Ex&CnH\mҦ3D(Czc?x}1@+5EFB6֟\]3HyM9xG~eisߍnPpHdu sQYF8c94ⲿ&KhȧoJ4՚9İ-b0<: lQT '*t$ ީ[\̯]~I6zdS6#@P0=jƯe^QT@Y(QaXah] Ӵ4Q mQ mߠ\'oo_Q mQ mQ mRo~c)_R~(ߠ{{(ߠ{{(ߠ{{(ߠ{{(ߠ{xBE$+IJp>\m紟SO l+&?6p'ŭM$l_Qf?Gip&*<.ǖ ,$Zl-aL$bFeX[r 68$P#j 6Q5[mH.I?@*5;Z#?屫r@j[i4j[i5m-oz>It5hLcsҊ(`z !{?Bo *@& KAcVIl;GǨb( \QO(qF)?B ?Bb( \QO(qF)?B ?Bb( \QO(qF)?B ?Bb( \QO(h=EP iifNz`$h@ZmY$9$m⺉eQNBKao}'V?€13O,H>s$ =j E=ԷFK%\;#A*HG@ VT ">^㏨ b%drU®oojm'%c@qD1sws@e<915YEhckT!@68nXEZ-ŭ n]8?Xߕ O}'V?b\xqldԥ64 Cns"D7ucVIH"WޡH`Q5* #GAV?"²<4}A,rQ4EUUp LdVʧa:}7\ (QEQEP:@y OFi3p999RkL<%z))ܚBK Dz|݈U_ xN]HmBE b+'c*~ևOPM#GrQq;QxH_Af5SOE[ \w)|=vxC} ұ}Qpz{WPuMBiEǯZ=FXU+ElN *GZv%ד$km,*ezմ4"AB?)e}`Iky$ @|.s=\>$V-2L-x ^5WUmHg)3 wҤ:P0XV ʸ~?麴RK2 .Tsq]22 \亅߂LUk$Lh)bxG_ݼ:d%͂:`u%u0##9C4K u? X988@Mh쥲0H9 d66"͎amwm芆 QӼKw4msE2hc*6BpO/NL.ZV#mz82rjye:\C%,eM<$Ֆ7nY XpN~ oi/6 -Ko H+=Wp[PqaLݕ{e[&[Pcr3胡+ebQjv,KP7=fU=@:?7dvjVP[=p1ʗv>V_n( 8'XK|)/u~,;7j_eMݩϕ7Q`Cv>V_nڗYSE 7j_eMݩϕ7Q`Cv>V_nڗYSE 7j_eMݩϕ7Q`Cv>V_nڗYSE 7j_eMݩϕ7Q`Cv>V_nڗYSE 7j_eMݩϕ7Q`Cv>V_nڗYSE 7j_eMݩϕ7Q`Cv>V_nڗYSE 7j_eMݩϕ7Q`Cv>V_nڗYSE 7j_eMݩϕ7Q`Cv>V_nڗYSE 7j_eMݩϕ7Q`Cv>V_nڗYSE 7j_eMݩϕ7Q`:j3 Z( ,HMV>X.eXΡ_MGW*|urQ@Š(((wD'2_⒊z(w Q@Q@P9g=G'U|۩g`kh|f$z{؟XdU罉[٩?ӿ罏@4`Ur/YFj+3\I?bf *[pn,Gݿkxb  nh< Y|. {}) EWN-ؿ>}~U'wcE#x@nY4`uۻTo*pGQP ׬T2:xe1y ׬T/KH:-2B((((((((((((((((((((( ?SQ#HŠ2(ȠaEdPEdPEdPEdPEdPEdPEdPEdPEdPJz4}(9$=^jH *5Ve}s$G AGu?@:M鸶 v`l,QBSAQ[xW/,I -),|l }=kXڣ}3+ rߞ>??FGe3]u+->alȮdviYJaFxg{ꚦy$Ķ*)l!t[B<3+mb˺sInϲ&wKßLF/>T:nzщ44{mOݭKB센0AHSR?:~/;Tsȶ[+!EYvHj7'`BuMPiM(QN|Sٽ^Cuy)vp#>պg˓D l  Bq/1Nv#4IFF[>'{v1 ;]fcUg,.@C3A׹ҡ]Ha.7:kFiwg}?r=>K}deWhJ.q 3W_njq A$Q DQpߧ?H ==V/g^QT:\wq$Lف a@51zEL|tJZA})iQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEb]As,pDESS5 OTU# Z?,!kh-?4Q@-?4Q@-?4Q@-?4Q@-?4Q@-?4Q@-?4Q@-?4Q@-?4Q@-'--o_?4PcA _IcA _KE'!khб_񥢀BƏF/@ cA_G?#kih}m?4P}6ƏF/@ ?,!kihjPv_㸷-~qmfϖ᱖Rt:`A ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?fotoxx-15.11.1/data/images/gammachart2.png0000644000175000017500000006410212616075370016733 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-15.11.1/data/images/mashup3b.jpg0000644000175000017500000002351512616075370016266 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|tonemap| 4http://ns.adobe.com/xap/1.0/ 185 245 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?úECich@w79$4/IN2}uC; ;3g>lj:mulp8]6ݒ=9u⑝nq,V0,[iMk/4*紋ߴY)`I\5֋TUھzƿSB.-7m,g \\ǯx;ccFV@$({迕&迕y >7[gQ]N;2H06cZ?GUZsiGNxcշEwENImC 1V]8ȬOZ k kif?~l4\-Z==.Zũ\[),fy?Uι.-ZBM,{U]ۙry[=x_ʼokPh9qڃm`fy>ٮz/&j1тɳQ6ܜuՎx_ʍ*{{pwx_ʍ*{{pwx_ʍ*{{pwx_ʍ*{{pNT)qqkT?'U=ꯛZB%J3 ybkܙaH#C?')|"PEiTq5(;v9&KmYRjVRoy14EL_?U(#9Ts|՘,ۚ&) VOQ<\F!H۶{Tmk1$E'OQ9_UFof!1vҫ6g2L_?GOQ+q$1He ;cڳ5/ Zb>[X[d[Ucgs|"ɏy'(c|%aauI&i0GQzޓee M6E$kcI|<"PֿORy1$Es'ͣͭo&/ȣɋy'( >mmky1$EL_?G(\hk[ɋy'(bi|9BOUzbI|O")r1|Nׯ<9ᴽӤ)Mq?0*;{A-eV6 @Q'ma vim~$12Niܧ":-,$EZ7(r=֟ס/SgM,\VRsyk7:-qKthm>Οjd7~KYВ{VYi6v+yD,{𣧟7y9+py-絚E?hX$`}Ӏz֧#㱊DrI/PyCpjs:T f.dm/=nht+UeHrѠ>Ix?Jnu K%&8.8G`s>%r7 vҡ3ޝsүcaeinVm2d8s҅k@?gn|}%A`xYr cm汖UXuc9UoAt^[SEeGnz־麆yj$_6ʌ zqMm=>%Y  % i/_Ѽsֿ SOu˻RjY]26(P8=G^ƶux^I".tSMnϩhgӥ o"2U2+"bHHuJzhp n%d]H^aT8i5sĶwdXyUR^xi3FR" ֥k>fb92z~);)49h7Q]^qjw`Ho^4IYد{"r5#ݵV̛#F7v]NKdڣ$T?*{9#'>]:]S'}z;闪ͨW*~OL|\Er\tI#DQskfypraMkxM)vLeYH}/x=u4߉/f[t1 8)R%I9)zWox}|s"G=5brǽ.[[ĺ;j" K*9pFH=>hPϪYk>|Ze C޼Pu?PY]}HSckZ6,Ѕ@Y(4-]ʫOcm*C89i۹0cv~![m սū=FHzy+;n:TY4Wr[9qY|GjKKRK.ә lǯ[sƳA"0 "$YIxt?جKonLm g?zƱTUD0!Vꮌ 6@TQEQE-`xI>gy@5?(S€3LS5?(Sʷe{k7gmߩϝ)[ys}MS_ңO8y󃑟Zchw- 2?u?tB?|~:.Ob$lr͌n$Τ0((((((((((hP3EfEfotoxx-15.11.1/data/images/mashup.jpg0000644000175000017500000014454612616075370016051 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-15.11.1/data/images/add-text2.jpg0000644000175000017500000007255212616075370016345 0ustar micomicoJFIFdExifII* z(2i%  CanonCanon PowerShot SD6002010:09:21 06:39:13"X`'0220h|   T|R ^ 0100 @z     0<2010:09:21 00:00:002010:09:21 06:39:13m])_ )_ Fotoxx:trim|resize|write_text|emboss|write_text| Fotoxx:paint_clone| Fotoxx:resize|.jr"zH   h   E,"\@C_ D/j_`IMG:PowerShot SD600 JPEGFirmware Version 1.00+((L+": ("DZb :X*@$E@"?[w(C ksrursvtsiת/ @0111 '0    ""  ))'$ -,(# %*.//.*% %*.//.*% #(,--,(# $'))'$  "        II* ASCIIBMW Museum in Munich : N E 0 "'R Z (b HH    !#"! $)4,$'1'-=-167:::"*?D>8B3796    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOx!  }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ԕ0nFzI|f,"%}E #bV=2JuK^]J{vrr#~STu6zW8ZKjۛO.aI1ֽ# 9D[Q_g5${%?VkZ:7PqtCc;siBcEFN|++BAsc:*]Dmas2뒾J~sl1%GU%K=Fh_>^܎~jqDTu5Zh)h,XL`A )(桖uZB}TӧNdBQ/-uVg?p5Y>~nE-טȷnql]9tXE/7c>stC~3gN]y )ia# Ú#'ko{C ?zkN :_eE+~V%H$lsOI?Sn;3J3fVg IOc^}^W6dRq7ga2Qݒ=(Eӊį*U9eG\TN)x5Nhڳe/c/as@NC[t+oȋ}2F1 s\^E"YzߚHd zNcaϽGa_\{T}*%Fwq[4yenil5)5)[!iHUr8$ǭs~2m#w,;9Vq4[5h۬tao<5n֐]yy\p1ցjL7}0*6X(ӕxwѻ-$PA!dddzc&%p2K6=yUis\$"#JQN*{#Ǵ#Ukg2T)1fL‘HZ 4Fe3gKJhͨڭ(X _+ K7bR* cEV;nJȓcw+p ckN[|3m5j ڲ@lv,v9#iUZvf^S1W̗rڮڃY$/˰?DRVi߭cY.xxJهNo_pyrso.d$) ҟ^M,݅p3QZhՄZSmAOzIu=Tț\YHVU`QYiЋ$PFzנjHf(FhLzT9򻶗WVկuSn9+/TӊfU|rGW̟g}]1Fkpnf)T,- Оʮ\"1 $v44@ ߑc2=5)]–j*vqzw*bgs 3y}Kk$(Tdu!FѲ.o݌C!qznǢ'4e(WWޯ! D4VMu 摪.Zw0%")b]vT!KԊV0Eסm$y.(󮑑UvvQ\} Meb6Ү')3bC\!2PuyS5z=,v(.[)pg:60|1EC1b;NqXN׋QcEdd#-8uCHh8ZkDFqN-ηM2D]gXRP+)k%mO~g_$#3U-uQ=I% ].?ү70IG}Yjöv39W* P+ܛMխv$\yޯ{I%i|%U;NT};RJvJ/yv48Ocb Zi _Oq=^ȦJyWhb@6X9NW6Nռ~Kke9N 3[?>yʐǭR3:o أTϠ(zz{s{Yr|ͨX|e wq53{ַԠF-M_CJ&fry?ZrO3OxHo,8k)lu6j(%gxȋ U6T2?WLp&Vs~UjuTN͙֕eĻ2cI+*sjiK覅_#֭+bv}r)әϐ\6;G?kI|`}5)ªRRq\qMbjDq2HzU'}c@525$BA94s4r)[Դ] qi]Ǧx.(Fy5<=hV&TA~*hpzi;J-wVqlpG8ޠJ~ⲸUm,lpz;2{A7|3&Jr2;T[6?2)\ c)^sǽ:}2ic< : aẸסsIkm%N^P8P}I+ƣ,KG\ݩh޾bA]XLX&'-,fSjSK#/S\ާ_g'l}ZGWة;ne1=86s͖@9}h{9} IlU;˶{zUAzd/QICQL Y9ȩ,Q֠x\/su nkʵ{+T-|Gɮ7Ԩ꡵2c`dHcʳnumY5 5l~FD9F,ǹ$;z~f+YZnOZ%5=i$5]}v !9 "Sى9|PݑqWKbXUpt"&X*םg-_)hJ|epq 5.8 - 17.4 mm 1 2 3 0 5/1 True False 3 True 0 Germany 8 6 288 644 1 2 2 4 C    ' .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQROC&&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?~ANғ"C8⛏7sBAW?<Dž$OqEg7e2i 8E+#bD(} Aڌפ0JyL` NE.9ԱA$(M4 uQ+ pE]-4<3qNJUЮ^ c6-kakŽ?u*(UMq y1bu[/؂Jus9BO_Ӷj}"_ES,Bzs+v%,Jh`cVUDvƊF(b*oWll/䓛Y4.E bE=ƥ%-R}%-Qz-!4$; II ܆&HM#8Uɨr-Du!5RzҘ$0{"IéM߆F#ՓsonXc-c r#xɒEEcQ}6Bʱl5 JbAq.NrrGk87c7]}u"ӣR⹛ovZ*><1MG';o E$zrre3cjDͬq뻂okc^:(`xjFT.\ p( b5cՈv@d,wwwnXP#݅gR 8SpW)*zWhT@i]w-21 y3}qVCһ}=QaI@e d_q޺[U`+lQ^@?qVϡ*Kq,V$Nȹxnci9 AJ3v*[j's&4Ђ8RX7!ۗslbDnc${?&wg<)JҷRH;~jI=+jQ(Péb8rqU'CMɕpp;cҦu# ؔ[9A!1Kj!V +@pȥ"8zNTy]d&t-B ?(K eOJ}X񗺝GrMrO۴Q5y?ַN$0 k3,An@T~)Iw'aG\i$ZmsNykq?`>y7{Yߝ&8+RlaCnECm ȃS!G֥ RfocOiL~(66)4:nh.hFpzSR]>.vuS2yZQr33xx&¸jB3$mdvSV=׊Sh]܅oj/[daʴ`?z?AW'p-7^)!$/Km.ט?j8]_ANÈ3z[ɭ+ [/7w/1 sHk˭ڷmy9Vz PvO@-$GT[;{O͌Ү܊@5F%+mMu0Iu&-ƚN3}2O](L~`L9a_ƲJ\bXIɦqLDS $6F"dMj~UsZFr̖Wu4ZnnsչޘN*YعbN=F~)i #9J)Sһ kom ̬f/4J!0hHz^Çsi$g?֛636Ntʜy,3? BF%Ը2Q+#H̻ + |%u3Vu4i T7 <}LL.DˡRs#^a ~a.}U4vVk8q ?1`ӷ7"9?cOqm4I+u:QFӒО'Kv`+tpNN^k&{IDܬNiQA9"[IjQĞ?,]JniԌ4MڪN=r=kխa[};hd(Ig%Eed.U99!vmUc} 5cl&,V:dH*57o"?+rBj8_;ၱq5&C;8W%ir]Dp/2d}"{ .ŊfҜ̺,N1$W(h0ȥme0B&f$k 7|M6JQ@ v9R[GiEp75(ݶW|`qۓr><5DGcBk&^!Uols֖s\]I"qLRRMu0}k6h"f;?PSRMJ p)sVcЈFcPb^’{ TO沮|KHVy[ZFFU8q\ϋdAsYH䟺+8)ЇYt;-xط5sksہ\:nx7%(o`c헊d֞Ƅ>)\j]xfȆ8DΡxD˟@qWKo.iwi61(84b1)hH v%Ӓ{I匩-1[oȵ0)>jEXlH1U'ԬmpU_?! [dJj2G$IwdSʲ\yղJ]Uu3kQ#41麢j (H\~-t$CbKێeUUp.W&kMbB׵Eo2̬0T kTy+1_tQBL!D\׌g-7ӵUsM"d6-^Q08?@j qmcJ: Su4˚mU`1 tA?nOɑgGiҘ}~$!# F}4ϵRH˜8ɫmyhx,I\cFi剏8"yG'xKUCɲ)S\֟ЅԨ_J$(;ȓG+nSfXp#tdJQхc>bmZ t.Bx'ұR=GtNbDOVW{,$~=ni-tZfY]`GZc`w7-NvؐOq'#Gf'-?N.'9,rʸ8Z@@K'UԵJV?a҆;<_aQN6'zKdG$/ӁqO.q=,bԏ=j>pnlfd sՔW>?"Ԝ*N);#ZlY&8AEcf\F}Y827ⷕtm!i]@kPm/ɷʤϧTF* ܊Qi`ڐ_=sUVeq#J[n+VJ} H,.*9n=wUX#ۻQ|R\p_Zݺ:Ɠă`69#Hi7 1HV>92ԎH-WD@WQH%\XSbpeαE4i1B#Y7vJO%Sz5Q.$EnE@p&27 V)9 j必u0n&|ƹU1W9/]x*cxC o XDsq$ ^b$ 3C-cb)i[RNn!5֖46oХB+sd82+V x-m1Vg4flbi}q+ թݗh$sxAnr?Z͖*Ns1ֵo}e孿CO?fdȅC~QW5n_i~Xmc]nWӸׯ\izU5*|$S?3T*>m?!\9̹[+4bS? K<}֎$NQG ~kU&l lStwd֟Ϳc~5}Vb>Ήͪ*c5,b׳DIE[2vLT"p [/U8wDe*0pv>gAӠPǻX <|s:g5E; h7m;plxfwm:8p`5HAbԞԐ[j-$}7%_n$gVuQX"k/aGM:]^T xI G@W*tVk&Ա0ppXy-KineDQk[_}L.إA YfUj~om !šze8!id`$V.On ?"ȹ8ha'Y9]%SEt+id\nT$g Une7fp *꺅k03i8q2if%Kl+F6^zM*v. =E_h&<T`eMXn^k{F(`VM-ts8B)T40kF>s7xbDHsT:ǽif&Ol_.w$ +d#GjOLW݌wNN;CO\ wdL?v5z^cbeure\Fn%!]>aUː DjEob0pT0MH[<ndV m@K2ll<1E*[~qpH<τ'9>hq$s#)KD՚ǢnoQ8O@<ci 9UV,?e__Hv7Tz&2ՔXB 8J± M_40J#VHוqm rJo~>ܐ= ]i%h~Op'p8ay'?更0p8'6IaPD g]2`1 P=5o Y%ˮ0IE yQBsӊKqRM\6:ad/_Sw/$39H{l\ҺL5kC`[,p:H (G'k.E<PTeBrwO#Y2]Owʿ:~m|Y!]8kYQ$'rדISjz !5?I[}Ƴ({sW5Int:GX̨QԓYkV1VS3.ץr^, ~Vϡ̄~ ]/usx݊[@wƳ5KI\_'2M[eZM(:˓]= 'so?dx(yW? *Ǜ07c~UD2 )Rwؒ qUs?E7O;[3u??&{-MFThL;}c@ wri͑ssSNwb?sSo cѷ=I*=G0=c[ s0d.wNE0ʤ:/_izS|9=j[:7Ʌ\ϞI{6kܘF)~&wpP޾HZwg=hd l\?Jlyrnָ0.GM^)8^R"+sARJ4c3#_JvGm#› U](zfcXi,G57Qw}XF>iHfh٢h#; BN۽X\"l~UGr5*NG{M=2O$S$zٝ,Y4POCY =Džf' 렏ʵ,+Kl2 e<ZXbzZly)=f\U MdvS*UU+fP]9HqǢJztCstt1tc^ryУqpp>ε$p?ޅ^ O"ech3:8(xP\|ujbrǽWli h@͟ Ol/vL@ 5,V U$-&,]\*[瓂ޜJRjtw6ڕ֮^gyR=15=^36+gKX4SΆ i^܆X*'þ)-YQGa~L[WF8SONTDcr'q^ޢo2@F'㎃oNDV8"fVU=1ooZ΋rm- /#(p 5~*nڕ";E$EM*o$DqV|!̤좂3v/@=0 a{2HUc?{!]w(B݃#>cI.{F?3Iw|ό/:K-.ronw` '45pH'F?Z5]:%J) Xzgp$HK)iHEF;/'~I@r~N=6):d(ڼs/?0cR7f%ɟfUՔ&IR 2“Oj`E{mAIm'hNJA4G`}j~e'߲F p[BZ^\GpmR?>o CPvt,纞Պn7A.pHuhՍ{IGך*-ۇY^#^#k^sO#b1׵6DHymYFAjcG9aR^kUw&}jh\FZQO 0u ]NrѮx]!Ila`|8gY*QI4sS]OJ;xM ;x7SB+\SzzH^O?8?,j֙Mr HSݾN/6q+bt}-g>mYj&rC6'A[U.e H֜Sw;dof?Hj}+&oLs{ӛyT_RJJ̀[F,H)Euy5JQO*WJOBad(זʏWwo䟠x;~Wȴ<9oϷXL]1ya/fܼܝiy\]rrj0`N=1Vm&Eܱӌ帐4.Wܴ0EYXj*m;Im5U0>BV/.7 Y`h>ն% zca)8J^1Ҷ2$:ޥQr9XOc5Qkhzj92:Uи9'cډ\jOu=e;OpfEڣ$QgLCV ڛCA杀 4OU+# P{@'O49@'vp:!霊 d0+šLh{Jބ4 4& 1U#/R +bc1B晉 &NiCZ@O7 Ni_N}zzUSpNqJ2F{hd-f YO|k~Uwag<{+)SZgoz>F 'Uf<3@ٌ9kVl-|ץbir&)Kdg*GB4IY EIdv|Ǚe\ iYϹ `ⵑ7w DVk)ݜ`K0O&{KǐGK([鈁#wnbjlE#J0js?ҝcQڶom5U`)6RM @ L4n=>e OE+si§cSMuF֤ T;O!] A#\tddi&  qUGzqPqzagqwU<Հ2O yT<_h-#sQWglWi(ZpXۋVngf 2Mq_˅B{yt(ʬ75WPܟ0y*q©(R뀑@g$(R$׽Nl6rd;zIV&3EBAsT% $`*6sFSI RL#)6[Y;HNh#(L9iE@FNyr)] ʘWM3~ԛI=IKu*1cbkTKcE[{?AP5j|1Y:("o>~vs>5[9PÕ 3֐9Y 皱R/|3h#oA$v~Z^֛!RG,*A"QQsi44{MTA*iBk֭Y{a*Ht?\pљ*z,CTU=ـ4Iis5z Dn,K.W.?~^ᅊ%U׼S)i@?MaWwG UCA֌ T NRRr9M< ]O4 @JAxcYTcՈH9aަ\@ n{ML419X=I |5;6Hyb1zGjf'G"۹>GOD+6O$p?$!h)!s_ޗ<Ґ ) s)5.% IԌFdr2 |MJAMZz,3MĄ7QȀ*xr:V YhTNet[%E,T`ZS)%O& r.x@{Kh-L& vR֗QY*W+c1YhrY>PN}Wu{[rx~1+jkKxGdU}>VI[R**=,TJ6F?=:"j8ۓ{B8#rqT"ޛp?;h‚2?^S_9ڕa?q7_\{R1z??bT q> pW?/ cp3hDU]wdZxR0wWr$qq?Ȭ>X;x< [ֳĭ/%#9bUyWRXuo%o'֪h˹E[Fk%f"^\AY$D5ҌGp)F=)^T"{B+ VE^O-by#ҕRv'C7𩠒:桚emOEI6:I1K1䓎f.~nisΣ{FғޗJȱGzf"$rI SZL0m{';_IE30xlnpv!=X4 8_ϭAac}gs]HOjn5n $"8Y{@a!T jK5(rz㊨luc=zUQeqa* {x-ۨ\JɊDm*}4KH&-:#?ڤ X@n%rRb':Ҳ戅9cyuϡWd`iZ8 (l].!v+UtƼ,k=3u][rl^w;4GV >hS֟pēYzMЍ9P:MpF(9Z#6Jc8?'[Jy~N9TP#E ؚ{OdysҲT.4ܱ=ccjrzkRrE- TgnԗϙU?4=k)A]ل9 mRg3) T.f&un!OCG8IbFqRxoWK=߂eHԩA"*&b-=i)P251I?y٧0s;#oj,\Ý$>zpkRWFN-hB}i]\<{F1HGqT":յ֖972gS$LS6' 4㣸ļGZ\rsH8nd QHy=(} n}n֤ =1Ec׊"Jw枣֥a{ՙ>)ȄM H2(ԏ$L@1+Ѱe$icR@=had \fH>™$2O@*y =.2O>8`O=*İŞC)\t֠Tɽ+g:0.Z崃D梮+9x5 V}! n%{ʅxHנ$gpBtQFW)9fm`zƥP0xr *}jЯDwӸȂXdŸ n&u:R5jVk0*}k.`{kA">=3W!k/elɜQWFv)5az,0$n{.vx^FO4%1J})*YT. U 8^w*sisԊNww=z##4ɥ=9NiG*sZ-QG-H4=sYN<Tdl$擌T6P҃E:bg{ݞ{ȍ?KCQga=mn[ֶ%K< XaHbe})TGqɲR#a}1EIL3=y4RgJ|${kP4`[pkoneP;nM1V+ 8)-MqtEzC,p/{0\wdyG I+ϫdtvd#iaNYF6=Mbh"F5;}.fHB*12ZOť̮Ui ֭; &qep0e }jW0aDQ`0Y$Y5[y$0VEUkQS9X.{+`õ^ct,+gni4"G/aX2rIq[3[@+C/ˀFpk9.(QdZ̈QUZo?R_cQLƮĢi܉=JsT7썵>UG~]Spן4d&g 7-$;=+E!qΣ=YQHm@{RNBh@U9ѲrqK*> {QV\ggl"ep1k;-Z!}\a/k\ ;jFw8U-:S3j@*\c$4sڝ⁊?ʓ\n/1IM,xװ&.3>aB$OJc*0Amk{ ({Oҗv8W\0 =P5`m0x hO)(bG~((0Wӗpp&*`_rE5_t8(pqQY@1ĊYRڞGi V#՛X=MW7V woFTN<(u437LBB+p:Ֆ\ɱڼg0z W1E [rtdQAUǛ9 U=1EZ;E1i؏sLQEtQE((cqD*UQ\VoҔ(1?Z3h:}h??@hb?fotoxx-15.11.1/data/images/printer-cal-study.png0000664000175000017500000002614312616075370020140 0ustar micomicoPNG  IHDR R  IDATx c!]nJEvQdKnQv۴eR5lDE%l-uZZYY}c޳g=y;=93337g()ʁ"dnR( }ʁ29@Q&r@9@πr@9P&(d6P((P(E,@kfƏ.?fy9D9Pl(w)S멧2COL9P(W(Dw5?_ʁ@Z@o~>ڜ~fٲe F?1F2۷ ׯ77m۶5'x5kVbΘ1C$ӧO>lڴɿ,fĈc1;w6&M2t~׿3gN[뮻tIO>W^Il0aYrL=̣>j&Nh֭[gc~7fr)4i"5gϞ7͛77Æ 3SNUzfڴi椓N2?M*Uɓ(N:ev#mcIZ)b́ң1ũx0_^zVZ3gTCK6m-Z`yK۟Λjժ@9o[FO?m,YbĽk  > ͚53_Z8 iۺuk?|`qвeK=mf+_q (͟{9ӽ{w `b0wGyw}E=a:Z| /Rj $ƠoРA$X7rH3| .))`ɀ8nMT")Go@q A$|r[)B{CeBlmu _|a@A=؃& ye_gwaG;T>N;X/p v|T˨P\;}$}@唶+O.=Inm=4oVZ]->d qˀrW^-!C\/7~T8w;L>ӕ͉'hz-Qկg6l(@ [N%c$WI.O~&  'o_{b3׿%J%\x7nXYk׮*}@k… H#{c+Ab_vkDcǎ5%Vٲy|dަafj͚:ˌ=Z74_}UCISss T;o?n0a|饗D9̮*K/̞=[+VkFD{0Λ7ϴkNܣ l 1H<[@ k <*veÀK7'YuF# {믿.կ6K/ Ű[Z5RGFL D-{;^Yߩ$58}Y oc=&.Mtnݺ.:y7|R6m*pa2{ :%AA$mScpŰ=)ŵ.|Ge;@#8B!ܴxMМS͚5"-r̅^$؅Tп=yv#8Yz!VsPoP4jhIzH-¾=L"l0@5@>FOEMB5Ԛ:u똚{4Uvu@"egt@㭊 (N0@dg@=AB€Ѱfv: 2)PD|k8"zM @FP \z> b#NkS+-;V. "p/>*P^7` mI^_%j̆ @v[qy#y`ƚ7ܸXo!)nݺ ĂHFDRLbG$?("ac|; }kmxE*aSȜ'䃃 s=5 I4iD>0s{w])cI .I Q]n3kQ.Ǫl*PT; f!E}̧Yfmq "[n}) J3c SW3qlE^@ʧ-K1C)FS {#*1"QHOQxdoRG:Gh(n q*$G`IL!GpI# G$ HXǀLlf:nf@t8 @ Ơ\#ܸ~yL[c:?N6L\¸bBQSU8sfvͩx1*r~%=vPC'FO>}P`͉L\<0 ^b;% :.VZYgb[/ͮv-u)\sOJ5ޘzZA&J!e ~Jڗ$X#q>?6Qr  tQP%<U7 .Pxwkv [( .9F a,Ʊ REYFRx\4~g%ǖH=*@%?$]Lojdf$'?Q_juVIPBH@P}p%/>7ï.Ŝ~Ew_@c.@3Q+ "%j*I# X12Ea(+GE#dB=CjN:+sΕ?xSW2\`N\pA{Fۼ"kފjOq5nfLttYĒuTGE=dv )٢+A8 owyi)`TePIELKOP3a>Crr,\NtB'JuX͍y1Hg*ޟH0`r,S1hɦe;9u{g_,D/! sΕ]QWkԩ9ߗ6d=e(@QIv@Ep x.b>&b9ap4HfsNlf+y%G7uM7SO=0fg!S~bh wL Aʁ49 @Uu 8P+i<'Q۶mG۷! u7W_-3Q0eb=ړST"ٸL|rs?GThGzajK Xūjn&E5Z\Qn~+WIII;N\,&M$lw6b< /P3;D-TȾuĜ0+ڻ6|ژ #? (`f7"͛;tءe@>8#/C!?!clR/YLdωqc ]t1 ]3)&o@yq 0 L>̋mۂ_|QeE= /)eΗP+Y^r)KI C;n^kՃ3ӷ / Jʁ8 {? SqOӻw2! ɕW^)B.]  <7bBlj|`ٺu4]T߱( >[5Su}n~+2@AF7:u2cƌwfʐ?tMTĐ<lժUsY[nie˖3ڵG1Bg}̘1C<,D{WADk "eh&q ߼D ફܓNeNއߜ+ :jJܕ{l+~; V} #Dw,D`Q$ `eQ,% eݶ^` 3pG NN1l&S~O= sW hPs4%Pdrڶrs =7%?@%ֵ eLpAe"t7 Z @ &,B:@TgUPfJJb2dY~!ڒik R?XxRհdSMo0`S\IIa惮 R{ɢ~nQ@4f*9b׆;6 8>R HTF=p@7T <u QswnR.cN袋R6+شi X &2+QԩQU\s%E!FNٳgKA-ZX:aIXeD\9Xxm+WtY7 J#E8cڴigϞfҤIW^Qw9|)SH?3"3S5o6,xo9R 89k%y;dZn@qGd>"ȏo_WRqv{96v쨊D&.k{AA ۝sҒ ),5\cc@DCE RF`?PXm$|AQpԉc#j<$ Y cP1QS.3&a`20k4@>(@'+%plWI7\C>`ذa e"#T?[OPցkϷ_0p|S>c*ސJǏoȵ@%t/|_ (1Ӣ[QWt^BBŦ@ /@67xy˼y(X1ыI-QIWaA?1H QAHwe$.\ )Ij T r`߱HNv~qwNAYsXˑ* .mf͒TV#Xb(/D娨CbGv;-ˆJ !~IP#W6B$SOE_ =$?.Q͙3G8c4XPYH躭[=O.0鳮r99 \c @jOt5sl.臝Wr} $.$6k8i-]]*Lz9쳥 en(摶( I@Pi!LZV@,TUIEs LL_/3ϡj EP:XEEq^¤)`ɜL\#j̜ L@*DU2[5>I#"S/G T Sj_̫A0PUF%y-bƁ0)bʕR}9g,$B11k֬)u#KM[4 f/yI'4}PjX(nvSrl̀e~WSOjCynW{ǚa o-[n1cǎ5̊tARDg_y=\nѽYjٴiLnLyX^\fu5ks Aɑ/ " d>3Ϟ34lP ;on$2\# $H4d`ī k±jLrtۣjV; GH C0߭*@t*Eq&a@Ɲo <cƌ1GTɓ'<0$YXGu|_ 0X~=.]*ލK/T*(*z

    ^zv@Hr?"H %C']"@@4q'@92 >s:ѽ{w V00t@[i„ OǎMVD O]}"I|"E4j(G@T؆rQ=y騣2#F0oYfqaw1~xQ?,C=p Tu8J$6l) $%GG2*Hd/rf NI vTF ~˜۲jQO7o;c.2C/Jes(1)H iJԏe]&#D#]0D7nP-0^btL [ L w^JXWRR".cIAg=)ܾ~́ڵkiyev͛7_ ^}#%PIm@@: GHx8@9묳$%h&^z`=vv[GΝeٮ]tq5EU,<8K&}?}AI#\pT #~C,C޽͖-[ed rGq}q7"qg&&M6u,o䦉n ]%"O38-N@AM?oTrH o/L'ao$b3V*mTDZ 9hBws9feLӦMcy'EL&ɄEݺu<`O0$? ￿ `˽!{BlKZ|Ņɽի~ 8|S>#G4OEشxa|=b<ɼ %m zѨGem$RalP(FlJHTz(Ȇ$kUW]eatMm&MѼys61I#y9,Gu⍲QDGsb$r+T)38(y;SfFl>쮛 b ,?jp xE`eڵk'~yhd0M>]b,e~Jp ؗ*6 I(}/P*.Dz;!*"".@"kLG-D|a=̒;B3k >\2E1םw)u) Rϙ~~C:֛lހGIVlg,=;=+x6Q,qX1Y@/_YCg%ϊM"yG-<6ZгQax6jQQ"[rh9? XØq\zTXtRaw profile type APP1xeP[ =B@qX[J6 VX\mlM )j`_(CGw`']@T@-#ĒD :hf`r m༰n3+:; N/""I,^~u Ÿ"8iJ6 CY,iTXtXML:com.adobe.xmp 1448 1362 IENDB`fotoxx-15.11.1/data/images/unbend.jpg0000644000175000017500000012031612616075370016014 0ustar micomicoJFIFExifMM*JR(iZ0230@0100Fotoxx:mashup|trim_rotate|retouch_combo|tonemap| Fotoxx:resize| http://ns.adobe.com/xap/1.0/ 8 788 707 2 2 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?( ( ( (M YI[P4V֧F!ChC;]uoEw 1'FWvB?'PWǠh6uj;[XZ( %}doTF'ܱof\1>oS@\ִ4:t}]59>dkǭ4B.<(? ?_ƃpte:3@9> ׇ)MQ7맹2k4e>q&j)Y:XlU6x@?QM>oV@QF+f1ȴD'֪I="84pMh^\mpw!Gkw:>[Ǿt+j { N(ޓ?5v/n*]F8椦+\3RV:::<# <X T ğlR_ #am4m6?ƴƃ@ȿ3412/i(4cA? 'iš5ћkV>l1~Οk4S+geԏ0 ?ֺqKE8Y~2aԢo"KY@| 0Q^@&Sx7}6F͕5>[gβwFE{>O\җ'{ Ahk>l+VmDŽt ̀9Z\sɱEz%Ý:Ak>:y6VS+1w}nϙtk1d8ue>bF1EQEQ@ 5i^MO0?Z- XmI^fpq$z֦IӱGiL a?Zڵ ijӚi --"g`\we3\'? 8aWJUfTlG(r= E8Ea>)bKK@ E6h7bZ(( KIJ(KH)hAp@ Km-:AK@-%Z(F*u(RRQE)sIE1 aSR5P7 Ю[gc8Eq'z A$W-2Leצ`I?!pTQEdhQEz3X׺!WZOdUwR1{IH"@C+`77Nq1f#lb=>ϧC![n-ԑ?*-N,)8$qT[} .G>꺍GpK獃 ~uzՁ;NG=0*Mx"dQ)~"؏򴘠ChQ(c))hRPQAJP)u PZ- ZJQ@-%(RK`%m!)GJ1JQ\URQq&z -A5ݵ5QKíBuṛȈ`SX\ uYmcc A R-- F% g{Ku DIo`INco&r`AK*;41n^C]O#޵r>)FPH=yu4l=;Tm<Q֐1bSڮ`Pr(_m4= f8M7!CEK('(!|ҏ+i*/ޓ) (D? Q QRϵ2ޔlom-;ktTbRъ\P(J(@Ԋ(ݔ H8.{P"S LW+)Vʠ i6տ$L&q\i|7J Uwrܺµ&c t ~XDPz!ձנjϭ4昬Weq'hOzjqT + /W]"sfTEP(tN e @@^:zo6{gV-D@BPr~8`]@*򽎘І׵)> V.OH#bI5Ty ɚ~VVf@0zxcOe*TE0p g%ג2_Sԗ_B \cPBb |Qu!龅8)Gn⼳ޒG&0>Ƅoh*?k=̾4A|K Q@JyJ}bIH~U *"Br6H ڠ Vib'BQմvdh8;xkVQr nߋM@ZԱo970 Of n*a?1SpzniRP!LғIb _~t)O7i}ELywT2}k3]Ѯp@y?P%wbjb~^ҳ!:mbv0ǜe))nw#ZҏMBꤏSӥȎ$u5X.lJNq|֡Kdl[%u-Zmc ;\顼. e* $(852PisL4?4ne"RQXNPCs`ؤms߭ݔr=|1ϡ=gGq}V7N~V@#mpK9 }vfy$r:hXdԏ[ԀWF{r,c`8}^jRǠvI495TqZ::a4X hޕ"ٷi9d)s8)zgw3zS'yETgbc'mp9TE=i-ʲ[FITVelq #Sd} aܦ;Jf=V'G-ztqW~΢ԉTd?֍VzJ}UC5qn($$>{zve۬$KDk/~\T5[ !ƻeie=p2g֭E[$O8<4D5Vv9ۗ fa P@cwA:|VF"`ŗ=ҶD2=lO=rvl)J(5@fkln{W4#M"ކ.~x֬1czWcDݓL55'k|oЦv&=!STAiGz 1 8:(  ,:uK}eRi?_VB(6[l)Y(@FHbU+rOT; nw y"D OS[qD>usZjtAomAƷs[KY cd䓳?Z.<+sz,Q!ptx.Pp:A%H46~\\\H?15 N^"G|-2/ ~屎k#ŽO`G; _j/4]2 YNcm%( \(@W?~?'?rJk &fM^VdÀj -OҼ٭!)$br2*?j6ͺFqj^wZ\GdV9?6?Ƥd|\IJ] 0 1OJTE&b֕?bP[]=*8m~numY{KeX67W` F03Y[kQaA`ܚLưD{1VΝ ~id8/1BN "?Hq.]\0U#iG<$S&E!F̲83A84J 2ֆvżFZMT k6Ƈlh=wmU}Rm␯/vsorE {]H瑎"Mc\K4KFy`qJ|EBu5BN0Dc-sKE N'se@l1mBcXmPA*ʶ4#Dfp0iDOn[OFkaZ)HbxBw\Ns}R(Eg cjTD[sSũ[*t@DH+j(=*FSOJpM=I4Y=An s5ǝ^|zοּ^GTF3♤$E[˦5o0! mluWū/ ^2sԷ *c_qӌ*5S)NOQElbQEwzJ+^zv5l1_V1 3;!~n)ʸNӂsPUWj ȨfV$ܹbV.G*SV!{~Ut?*+6v gjw!SOkC4k8Km*OOΣΚ;i#N0Z=F#66$!P3ItPH (5iGZ/ `KF8rS:*Ymd FXӈ" !YҖT Z+V œU1$(*fMfuGeZv4ˎ_QȈ @zb 2/m @'~x붞OJ#4yk[B \SϿmh)NĐmRlXD{h)N"H7MXK±ZHHP:⯑ORXEBxvEs%r 6] ߽h2gp}sYZQn-ikPl10\2Q< #+YX|E>qR0n 8\^]y\㉾@ 7ZŌv#N>γS[f֒ZmmPvd䂧+uE,zV*b*Lq[3#p+V!G9*0#?-DzsYnb,.g7(,3ixm^SCpdCRfJ_"oͪ, 8Ԥm> bdŷ}xUYErf8UM?fmyd>bn5 еSWrU:5mZ8BnVB7.RR*7z#MS &fA? Po-z6a 4{i%pmf>X 3d{u klc&"bLIbdGhI}:γ'1n]M3x?Yʉd"򛆑f-'pcyu#%ڞW|>9$Wn5MWIHv 1e IlsYKDhsk>;)evhBWys[ιj-R 2>ڎ.n/qǙ6:'ž)ӭVEsS.QϨΝ9Qf:[NF|8uUɑ[N;$P=\Nt0she)ChG X$kչh P61tr(,\p yYWIrٻZK!cd+ɣS+mzz3^l-] .Oj2T#`$­ߠHFdeS}](|2*jQ`NNs[2, JpxX繛h4;Tt]ugcVݱ~팟 ٴq(SX|(!FO+ Ɨ>*VJrR`L&p\aܫyF;R ;h) >Bg 'ܪCz {b/v7(cҰu v H#6[`cnf; L-kLn㴔]@[=y=:-6${NAA~U~ә^NSONeеEܓcFEo[J$Y%sW/cy3*Al~RyGv ]wvD0$=׵ 3 %Ⱥ֯|V\W+Jm[=n@O9")gcx4Tg-HF1QV:(@Q@KӬ/jgV)N{makS dp^:u~e8qU#~4뵁GQ||V,e?}{q f=R* RHM* gQC3D  rja,R;b[ֶm|/cfc)݀FFjطu?(mfeXܣr[^iIϙ'<Zh,6%ۏlVL *S uewR)GGOoyess$(b.t$gT=8gei? A} .Iщ Atx޿ Za8Yq}0(#em!etJDܟoS~LqVA JX+M+EV-HV-a Z6>jkg9,2 )5v#w35מ@URԒG]dq ?ba* u?\MY!hR8"2y C6rOk7K+qZWvT74u m9)Aa4q&YAVir4r!i@2 @{WjɝZdzq#w2!;էHTe VYSq5F[Ȥr)K4bPz3gϥq#KQu:cVF{SZI;IH-ɼӞ;z?0Auh67S@ :d~5[Vn W&2#=*]h^ .LQ`W2k*[Y&gIw`q&Vwծ>ۜg+VO+H=1Ƞy y%4UWUu>YHˍQ|=<5,:T6>1$K=Tb?=|]rX9X^&4HpAG'+;\F6>n/6OaVyV^Htfߌֵ42_1!H9cZsݿth_.9Y/s*" U?{a/Weʮр}+Ѷ8s6sI#+o]%~k5x;Gzh,j֖pcy 1kiUBUjAAе%%K<V6ηQt :VYD1G^Q߻⹿yLn:Xztp"iiL(Q@P{8O8)흴8ӉcNΌ긍{KVK4  ItW؜ xB/ЊMCViX1=ͫJad a؞xi"n!i=is 2Gؤ ƝѬέ`Hs>J[#zs*l>w9?Y ^O;G8HE 'd~xop>vʑ$Iu\naшYi6m ?MF#5%XB_1d\ڼ2%;#suW'$xhĆtIDzDӿM B@ngּ5{s+ֳ<>;dQ`1N »~ʍk09dg8?f8.mJgSf*ڄy]"R6a?\eu2n@楳zpEcMU kv;e JEɒYrGNkR)2jklٽB-mER20 /짞;ec,+o̶:|(3ɸz^]JW$Ԉ4/ iV.JzwzxDfG O_K0VHnFz1\ĄGd+Y@[$O+osd}^9;Ep`$u5kQY{}4R1*tԈؚ y8?RkdE*?U$-yc4Jt ) MM^ee5(%=OJ?=_Lkp@e[?5]n%M`I#=43i\eQHa@@JT<㚉-#>. ePFtQkP++{t sMm⪍:CG+7ԨIjQ4OY]ӧ5'49[@DS.:yRivIk֍m%H{8"ːzuUɻ@秂VKQ5Q;0sI۟{>?GY1y_?e^p[f!Kd=ƔJm4_GdXy@w!T沦k2N7#F_B#%Ϫ9jUrN[%:N@,5i@s|θ.9aD|H+zWDBa*YrRӭR=zZd]w?զ?Mmʼpgr:qvs2YILV!d>V:kS1qNG6T)rTIYLFB9Zx~8FSDn=Wgpϥpk4vœG?΢J;J ֬(#i#$Ҹ&q?A4Bk 3ұʃB\un\QZ~=!ZŴ+iȧc(~d ֌rZιO.23/)W:hhmʧN=:0d8?9H)#lzoqPL-4ܱFd@%{E)X*A8G_Ʈ2h_> T}I4p<£쐒~0H$S:ԽƄ>#\g2.mkYsmQ?Vo*9Ѷ^۶qP4 ĨI0X4yWLh,x;եO)}4u b3?’{Idyj ?!JsIDblÖX(LT)U9#'5[J9$kZ\8D٘#;ȃrlѱ=ibՋTmsL+C֠_voB19ghI-ʌ,U8=O UH#vuCsEZ$'8?]ƟEu/ϘqW^ ͳ X7e cihVh = <9¢ \bQ$WU%}ޅk 2&ʐQ|/կ :Xyt8wt-64-y"t{AVʬkY֥2Ao$_ .n%Iّ~\=+^(Uv `0+`x>G $`<(Ҋwvoq6e,Z8UU_@M71 N<5kHa,#O~3YwDK`+tngԺf`da`[xWiC$@Xy?O ˴0ea ְi-5l7[J)O ;+2 P1^pIAbԒܲnحoaF*ծ|˹LpU_Դ]~;RI>m }k(FKyuϽm ;Z׋%NRbqvEY!K5f4_Lkk茒rՕ,N{*AsǠ@7##)-z5<}ǭZK6YDV;g ;$kuXr!aq•Ju_K;č]k7i bAsVgH>d$G"rxOwS;6GzC2.i"-36E֋itz5 bP$TEJkS W5=:O}nsMsAO݀> -ͳ˘[ FW n- lG dV3ybbiH3L=5F[aBdi `<1w'ޡWњ{0 ꧀sVee zW/@@8u i;[I_)^{(sF]qsH*T^HծQe?\ߖ )Y5DᝣZ8SVwM?ſº]EՃKxm_BA@ӦeMM]`Tf_ߒ6=[FwZ0?j;hM&^lijw;JrZc6 fG?ƫH+MfK$4P:~8vlܷed;=jA D湟LeUU܇!"aTF GVyqnYHbH'j8 nky&d̊^*o㺞KWXVR2XF\4Lάx|%JF^¦sC6!*Okjl+4hN~=T}MiCQ?bb:ԌnɹYs#?*EcpieVlmۀk+n]j6\w52Ygcr\Db r??D0 ƹnqBjQCk{gW(G;I4Uhp4QEd@P(;MjEg99e[\;ݟyu&3:8'N**.eaӴ HҴrKsT!S;<~U%沷JSh v Β3Rw֗R]̀8<%r:]w +zp9=ّd?֩iZjs ȗw;FsҢ4 f6F+x 3m גx=w<L5yiop0)pOruj1Oqqf^66s驪IYʳdA}O^j;tR~Zi:cibe$cφCmp)#IjjNoB=ܝvFMXѦcst`<9-Vn bo8QW`?g^Oq&֍؞C cKҽѩX"t2?sq)WUkv ^ [KS_bp:5Y|Ԫ'/J>)@朹0&ߟc@ʒųOJRr8X~C=#6-sUE݈'Xw7d9VT/RMD8m<;}*B,T,x{7CBNBg2Wω'i jWךڥՓԵض!Վ-,jw:7SR*AҍҥXԺ-B&mţ331+u#FRPsRɡt1ӂ>2 O:nj)ˎG R| TOeQmam~m* Au&[d0J^EW:ӯ f!U&'}y88c'V{4UrTF$Ζ(k7\6}[hAi7l\qtCLxGu XfBO֯/-N.dW?μ1bGeTCNp?ZarOo|sKmړAuF?rZIZ@.99#o&@rq09?H[fDl884A(ʠՔW>GUW) :9'Q#XPExr\GssTn.Wy?;c'RI@x1Ɏ8gTWyoҥj*LrĞp1DR}5Yh|׷L&=iUFnta'?'6l:pch3[g]>5Hṅ0dzes2jVU&|7W7IjֵM[;!`~W[BQQ<+ƚV )TOѺywmck ־~QjTUff9bIFyuIc%ܘiTGK&L~@VfMwԜSIQH{(T?*uO P)uC_ VC9!x٬-8W = ݆P_0 1)⨒ xWmdTkd^ ,+uC _ _P_0ϱx1WFBU]䃻m'k牑~?ju }ǫ/3_&G1=ju,?mxq[DE>!`#߯FfFڀ5֧UOͬYUgZ{)H`RɮUv /U/e09@pƣֵl4@@x$ڻߤUlUMg=4.. v 䟽[^񜧮;FLg?ѭU1Egd53Q`3?OrRLd`?>H-/̪ :T^Li [p2`J )y1p~TLI}(@1q*Co Q@Ѿ'ΰam~*)([}:wE~|CEib hoї>SrIbew Կ憎[E!LmxXOIx`y"`#U%u8XNתd!-@DKu`TiS|?€ڱj1B@qPb)q@8@)hsKH)PQ@ZJQҀ()R)qLQL(!8$Yuf8WS܌XyA QqX4e@;UK" Dq$o*>(q8s: Y@qoPk[8HLIy7%>J" $=Ih2g 5GһLN}Q-npQEfXQE^Ls*Ν\*уHZ~xөƈfjtUM,ǚe։!]i-X١HtaLվ- #,S5$ڲDž9񫚽YwS"+ڪВs=Sa1\ q(Zj= zH3'54񬃞FT *BVQK+ bQCINE.(:;d(<#iNGOaٓ(; Tc--&)i4 NRN)@b))@imH'E].]CT(G*gxn;g~]!\ Ff?=EU%w=YX5`p"=) =D\o^m?]s=rNm,o&.ET (\:wp"x|3̍V㗩7ݨO'(`jEܻU98U7FdtPI?eH(SnGb8ЭVA  IYTU %:L09ϯS/nnn/vMH$8${SJ73ȎqlF/ֻ J"Ildd 191]<[\0ir>S^-E-djR"hTeEZxq@ i>zC RS_S|ҋb1~ycߥEIRCߥ!zjo,zycBfE HH?j*ռ1k,4-0D"0J*_$x~T/z QҥgPb'}|(MK7hSctTJ)B7ONT>OQBjEҸi4"YBSu'a}O#lV2K0ޣ Ep%'4n4[e%z8%OJ_Q ;hLYBZ@$U6F2;B3V%К;WeBOGOo092?\=GL8)'qtHɫ3BPV2;Q&GA8Y5l orLW lʎCm`p\:mSK <7KjuCe~l%Z +sS[Ӑ އ?=͍QLI})h(=ҔR@HhJm:H4zCI 4v@IO izdI_1vvF?Zc)Y[$1@RpSE-t[JmpD%T\COS,N-:"'icڧZf J}8XԂWR݊Si (u1u_!D#ҤX)k$4͸K4uB7Euſ$k^i.Lgo(?CT[Aj^E%K9#vBSvH[~|D\iG7h^%#N+-[_o"+\RXVXyZnV@dU֙=jx9u9PQWL&5oc9 [wH$ }cP}8+Vlj+R7kpӍ2OfDE!3=ZoNԔ'MQL~}?:Pr@_zm9:x4ǚ8|}OOOOn5ѝ8^O@һe5},LP y4i +a 1'q\$c:FDF2$oݑ=)s6v[ҳ&Ue$G{ X˨j0ےGhuV52k\Zcvt_Y\+Gq]\0c4bP74f)7T\$rp:ύ~Ayst~f5Fu{S` W;H\[K!;Nsۭb2ne26:rO|zw4㺏ZCE8V*4rZ$#rAkR"vw,A"ki%-I=?K:U*-_=Bm/!d\Q^}˛=6{XĨG|gSjQ4үihROB0 FASC\ꑟEcPA5**zcK{tQD=?:VDFҌ|Þ=)٣AnazgژS3Rh p1`~BlOpZ b[P[8sM 5e>\i #J'P~PQ=#P$'F5KCYb?N/M&ţ($S>Sxjn1#+8z.9R/].nċXĒE@13[XjncAC֌SU]o.QIS]4@*b?0)V1o" m]NCO$lD,a-+YVnNAS[K mW1qGvx G\ԩFmYA&jZʦԑ]Bc8jEbW:n\j7gg+FpVzJ"ٟߏUDY&-Γf cTzWSWy-vF1+?ujaY89:Yѳ)<8.y=I&Hٞ=r1Ovh%QQF))SxRR_Mg #iw ~c\}+A<->1 hOMNBDnfrTG͑9utۥ_&ߗJȖkW(E::pJ\]\eKL?N}'Casj {{De*g_j/ne:Dbg)ц"h3)׷OH-$92( 1ϭd-]@1e}mFVv-޷+HmLڳb) P̛,Ι,4ȸ(덧oCT|I ig@,@5qW2{| T ;UךP22JsKH⃀8JzP؀,-"4 bYgo\ Lq f]OP!˟ORƃ2c((aKq>hdJE#(/Ji Wwwl hF![:soY ;cFyX [v$^; q3$"Rvx75Cx]Z@OXmg\u u$WfC_ֺw5mI=iaq Ќ[>җmM ӱ$;hRmm=bڋZ@ma25RV<%WTԖbDxv3|W2[ oe+B*?ֵ.Eto.ou+J`6&`0p1PI2dvQƴznYDgnzԗ cr7Nם 8[P};ֆ-V[Fd%#8؉EM+t;ȡ]v [.H5Jxh+Lg]ٙ(ӕCZ2S-YL1]*p{sYeخ'.ǿZ@T)SOA{S`bxA?#^+tQ jہ5>+>5㷂]X H1c'(_F3h @Ρm /Ibm@6Z;\xX3שQE1(. }rC*XaW^t&vd;0N2> 3PQn}ꂪjK!&31۩5rs F@- S*Ar{ᣕT9`y'#4p.`*#ee%AT0 ' TѲ,p]~zXudWE #xGT؞qVIi2}xIoZ2aeaW%&ιȣu-c1lZѐef R~M"Ӣ/[x#׏TȚM~vTU>ҫD!dw*&U&,Ǹ^HA$/ƶt b>?ϥoG;p82jHhHqZX AJ'4Vʆ:Bd4 ӱ5.PX.UR|F=R(7#<T2S=&WB9"Ѡ24aܽE`:77q;1y;Ƿs޳DЮ\ҍ,NҐ 9,z*|EX2_Ei1cʜ;b-<]gu%R ԱdÜVr5H>RLt]7h kQ ɷorQ#:Ռx4Y&@>?>1ӬvYzk=Cn a/6y-1ȫ[sקUȥvYkvI * 9ťŌfrA eD2|^UpFsvdeuR;dZs{4ՙaTzJE"ѣ)*>J.ߛl7OHµ`iZ`WJJ MrmZ 2JRQWd_㹗Win|TFϕf.#6wa8J=ڛd$Ӛֱ1i/̱P1"xמ$ܙ%$%ߵ)7PIW2d>c"{-?Qi {*׊+r4#r>EEÝ2V,lХȾ_c']MC[&cFGE? +:$L[.CV }_p블u|m5͈D"=l$)IvuV:ۡ {d>Ҳ:M>A3~D_W$iFG=? 3,|6\N؜:f K8C=yV>!Zf,8zurWLI ?![[kRzṉtqû}?PEI = Z\xr#$I]f;As#H7sN\TYi&`}EjjD?e"wn JHQ VY|3yƍFG$*hשTWڞo:\Ms)Eչ1;hblgnzJQ5H׀Gef"sSI)ng(ɫ]"828?jY=XŖLݏnJJ3, t?J+BM"4SK xa(iؓԼ]m~Fl-s3Z I-וf۷?u>Wj}g&,8>rTe[ل,hxTd;pJin [33oxs֧xL-y^hVp:#KQ8ϷUk `}:++t5{" ܾj?x;g5ˉKb|8W9i(;ETSo!mOd>u1E;#fCX[SQE\?Wj( ?W>u=Y~t}=_󩨢.CX_SU%RGicB9 w`cҋ ?cΏMj:d!,~X_sEAre:,?e:>u5Y~t}?Wj( ?W,~>‹ cΏM >K) C.InY,~X_TPá.CX_;yi2?]ޢJل[9_!l+&ټ`'$7hXXt?SY6֙k\O^MXa9v,z\\Y!c|zt&6Q*5{3SxQiJFu6qlA$dhBX$u)vH ~j3,*$hFӭiNʜN.ƕlvlOugCtG~й UDYKDz+`J4sёԚgMUbNKcb8+}?*{}j :=q8 3SV}ôJsZGWQ#Kb#(Z~Ӱ"12k)'(V2s=.,rI<{si-qpwPsRwhVWjhPF$ҹgRe0E(݅--fOYp^#Ƕዙomœ'?q֓nJ.(ϺD>[GA-a[8E>~i?TwEQ@Q@Q@Q@jf55nG>.>;<ꨠ"ZA*6!f9>sۑif[x49&V(G3xzQ[QVIc6K"QtS QHaEPYΡ]ZY*a$c#zV\-qm< n.I _n8JqqrڌjQm"CpqE8<5i{dxDW'F5Y-5?:<7eH+XOPuj𯎟5אڽҼ3v??631}Ӭ'H9i i^p5aZFtQ#6S>Ԩ{ &29 : ͪsYW 9Hm+խ٨E[XGP^S, Ú tEk2F+{M㳄dqDtU+" T6|T;-ؒ"y.z"?Z^`2ը(ʟn\n&ivgcM*T.D7Rd{OaMEŴQđ zd_ƤxZb8ڻ#>Vs8n"ܰ;d_*0l}H?**奱ce!AV&KNMEY tUrH]hǀ9&6U+N;q-n9w5Q~f2Ns6 [s)9a 62gH>sh#MPDŽ?ݿƥ\$ 5X]ts})BJ0]ݿƙLC1#hX}!$2P:@Ll~/`4ڍ.|| Ś1#%X{q>4GR k`K{, ZC$ Ē}=Mgf(³OBjltY[',&+ g&[cPXIt}<ږ~-zk`8G.PuG$׏x7}ji?MldI$nR"4 yK=?/m#OZ(=Msއy-*Gz `G}tUnV٥Q3uHN?\{cW7z5\ N##ca. Jw+?90incKwq܌ LӬ伻ˆ,om'=ro5+Itֻ`XQ$ATo koyPD.V90P=4uZAJZVKuf UC`z}j&InPI;~>[2\b 0>QsIZK5Fȗweʪ6h͵C ަlKbӭwcZ7ӿmƭ ȻwR\6DdlDAPp)w6iZon-]FHř1OSMhD 3(t*Yk dƀ(=ͤEk]jqpN$k4 :}}/-g^I{wDЖv2m^D?0LhZ+dydmrj3%+H:+4 {>7I icD6\;nk+Z3[I6-yLlȫ8u9YmGpXϼ8*U6y _ٶM]:s 4oO^5G_@mƁڟTn|/̓OWŊŨH3pkVfPۘc$4R=:Ą5 \ýeY{08<ҮElU#t>>{ vK0 3iqF*\?)=?k!{u?W|tt (zmb[ANP5YwMpqS^,m"\-Rqpv7 $B8IAPK(.X)7u\͝1 |~ActBz+. hpGS*ufgf!+3Q#ӐӟzFȥʸ˷W. IOoNS#rYެ$9;VǨ?ZR3oGEX`NA?IRNOaY I,QqND\m_oz5ZDBYz]:*~ QT̮;4λJAfFWwŽQsEU Ϝ_+춑B>DZN:Y3ۥi&1ؗ%\TBJvt f~V#n1txA5@N_=;ЬHzT;)IȢ~ٶcBn&hcuk(6nd9V| 5Ql2ͻT/rrLTdm| ?|8gΆZ2$9GY)=͚M+ٴ+9 !|ojӶޯB  |ڳFRX1:֙/bnWcTym谉vh֧zGTW1Z#]X8.QTKs \Ȧh3?2БWFs!Cq(/A p8b"])  ;7?^Kuf}@Z"haI;m^vϠ3s=ћk4fu;;?Ku_Vwmɹ\*M͵ji*ƀ%I:6UbM&vom2ǥI%BR(3ˑRrAPF}iU-妖vrB*v ?~ꕟ>dW+pc8.vmߚk4uz@.'?76[iNkM[@{/7?߿36[hu4 Dz\uuzu GQ-[j#nnma24֌2m,9hF\vxw?M^ K%,U?)=?k!5GJQI@LG{onL# Դwcp=ϭ_&FwFgWp:m.na)E*jASD%ےqeMl!a0!!@ޢGOɭU[S3iI$4Nwq6ȁ*m!L8#&=( ϒ{[aμڎuQ [9#Y(FnULZy!D3eWWV ys}u+/8<]uUՎjN ;D,上6IS~U>oe8'_!#{Cigrd#瞦;{,ckQ!f)m{}x'T0I?3kkh9w8)D% wd^4ʊjIޫ&Үdbbnif))j$G\V͜'RMI׃SKi\ d[[nXQ~2jٞtPqֲ|m3VM8)-L7d_m{IϢSsۓooon, l^%94v*)5(7W&C@uiBGDCp[a4Avڝ$BT {s'ޝG%gj;mn@564qh0fPq}Z&vcb~X[ MĚE<1c\Rm#~b1|#h ^7+kڹG?bT`BuI5{=aM!S&Ozĸֹ<e,HnH6+ecd`~۞c36kՅ1P% AUZǨ:Em-Ĩ#zL~%(h4Y$>քOhг2BAi\uzE[P3/$ ;hK^6"Ag{VMG4~S)@}hX۵֝yn oLQ$cwgacBآEeRn6g.x{UO_ף?^!1MQT8~nA'^^3|/̓SXŊEH< ׂ1FC;ncX~b{$ahث.F2}|&:䗷7u뷚`0s3rN^ _#]y O憖R3`3kþ9e^CBj:fotoxx-15.11.1/data/images/batch-add-geotags.jpg0000644000175000017500000003127312616075370020002 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:tonemap| Fotoxx:resize| http://ns.adobe.com/xap/1.0/ 1 2 3 0 8 193 447 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 ?DK(2(S?d)#S'm @?1BEXd۠ OӮgiˆQ{?Ҥvȧk(8>"`A"1ڑάvޤǥ8yQTu~tz~Z K0ʱzЀ1ʋx| ƚyimW!A>U3k /Kfrqzt @\2WhȥpEuu47 P#|Z2\t'MF:) R  ^'t#s!7 >~M-Rp3򶁳hUźhG&Gտ?Trz xZ)&5TA<|jɦ#=AVo<% b=Uϲ%h6H"HcFD5V eKGmVU >asHc1l!JU%*#azScx2)Q ~Ush,hv3)uV+ʒ9JoyQw`KG٠KEyL~fsh9H]U` \4h4hV8U·P`ҪD$zQW~Z>Z, B(`( *"*UAZ,*fտAZ,Lњh?h?`*fտA<>bDS8EuovǂkHOqX]>icF$*qvKf;\() 23QKuo F!Y'}:T ]I3~b0sҮq)^ju03`) 2qɥ+- kt鹁_ JKv[\q`4x͹H"2JQz <Gz7xok-1qI1 ERQEQEQEQEQEQEQEQEQEQEQE./E"mRm5U[Pbo!V*A gf=!kJ- @+7$Cy8Ŀߞ+nC1S(l$'0ߦGGFdbS(1#uq8,띘]'Ҷ? ? 5 `֣nÕ~S< !̃|((40ɷKI`)u*[FHǥn>~sBQϥS[V@'yi;/uӹJ[@1g=)`ŤY9+QϥFB-f. ~Tkl BmT-QRr ŸoJHmv("G~@'S~~ ,lYVa=21ҬQ@ik;t,S((((}(}(Ҁ (Ҏ}(}(Ҁ (Ҏ}(}(Ҁ (Ҏ}(}(Ҁ (ҊKotJ) ԶH*y 8F8JYDȇ`Ecۭ_jم'`to'#bB<'b4F*dm ߳M?)ӥ[m]?V٦h4?MY+}oO&M?)՚(٦h4?MY+}oO&M?)՚(٦h4?MY+}oO&M?)՚(٦h4?MY+}oO&M?)՚(!=(vۆ 1=X5+>in% a)I +ﮂMr%QSy~NXdsޝ-Z8u:) Z,6(wAyx)ehhvmu^I.ѭ.Ii(=hVE՜B |*[85rqϽW9fA(7sUx;5%zEh٦h4?MY+}oO&M?)՚(٦h4?MY+}oO&M?)՚(٦h4?MY+}oO&M?)՚(٦h4?MY+}oO&M?~*MYu eZ>2Hlǥ6s_P 6;8?Om5U%0+MeI\tȦb$Id29QItPsp1dAO>(B>)r}h4|?Q=/>ހσ{G} 3a j.9SP&0:уFCm$qƒHrj_>)p||?Q=/G@ =|?R||?Q=/G@ =|?RFO'yhKFOB>)r}M>σ{G} (4g)r}MYT"[iʖq3rOҥzVnLJBҒb)wUw.ݿR: b4^DB'UZE \H[$wA1F>n*K⬮#@Ax\S 2i^Yb}G v;, {tҴdww**9ϭ :a"+v})%m;IMp$K8V dc#s/|t(&EfyFO#K(qU<N b'GءIU(ءIQ(qUf(qT}\DUY+}\DUb'Vh b'KnIrO<18W 42v(I'PuZ]lrd>@gW4-s7HʳިPʻ=q†Iѹ=?~IEPEPEPEP?[x QHfX kG( 9=akJb#bٳM1NT"u EBh ! )P(V#8wB;1@ IBƪM;_|(_|<<_|(_|<<_|(O|d V2Rߙ>PMtICB3N#"SMvS( t:wd.UTL(BImk Cy*j(L[6yIݣP4*:E1#FG@G ("xu}ȡNK/d Z(9'h9'h(9'h9'h(#b0 1@Q@Q@Q@R7[xORNTÑ,׿?bmʤv/fE7bѱ}iPv/fE7bѱ}iPv/fE7bѱ}iFm- 6/f$yOhfԒ@ؾ4l_CnJL:P1f3LZX#"PS=P}hؾ4y'{SU7 Ąz@l_C6/$lwȅl#"d Z.?b"'[Ic(ؾ4l_C<=Pb` I*6/f̉ 5iFz/f$y1y'{bѱ}hO kf)"c}=f3OjK#F;z{P/f(y1y'{bѱ}hOG缿 6/f(y1<s 'f3Mv>?@,U 0_M.?b/yG{bѱ}hOG缿 6/5bk )2ǽ2eamtJ) ԶH*H*(((((3@gƲnaӝ5! t 98)vx#z:3=o.qmKQ1 <j۫>1G@)$gx5NT+A&HX;e#*;yh޵Ge".4K*0*|3*i4ټH@ j# iQ+-5b㶹(ɱC)l@sh\/̄C4 )y%,0%8?ң>SA-M$jlgWae ;+ԖPe$z Qʋ?%Ѕ$$42T>'ٔ0F^#PǺU`v)XxäIwI$85t y*çZ}2'wl@gA\8h7냜V,伕W `'=i?l!۟qҝ6c2v@-.L:E`Lw K;hW[-n1u"J 6[z-x洢p €-V^#G"Hlo9+SU@r$['4y!gD$늱-{x D$we8lx֭L Ueum0͵ԁ+.܉1py"8| @[LmJb|i(FQ;ǭYRJ.RM'lpm~YHQWS0y<bMYNTc?h-wr0V,No~l"fB!%Gp3L??Q<EVԱqK(W c?ږ?7P&(aQ"jX8GgɇyG|<DtUoK(Աq€.R2:OK(Աq€,0$EL??UoK(Աq€,0(NTD9DU>jjX8@)8BOrWR~? ?,o >L??Q<EVԱqK(ϓy0(U,oR~? <EL??UoK(Աq€. J:OR~? ?,o >L??Q<EVԱqK(ϓy0(U,oR~? <E9QPa/ږ?7Qc?SKon`ynۻ<֊@fotoxx-15.11.1/data/images/slide-show.jpg0000664000175000017500000023266512616075370016634 0ustar micomicoJFIFExifMM*JR(iZ0230#0100Fotoxx:trim_rotate|tonemap|resize| http://ns.adobe.com/xap/1.0/ 8 1199 828 2 2 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((R" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Ṯ!%Y+en]Sr4kK+.o +d6|0c=yˋƔ&T&cJ|R8kU;q*O+ƙ^śɧz#YrBwRfPrltpI Xu{,ٺrϿKtov=VdP_`(F@2 { 3[{Mssm>Ffy1HOMm6I4Q݂C }(OfT).nROu U8UM-G2</;` [ gSQ5.-䅞hZ66@(탚W *)=2@_!D4A`q^M2;^\UM~XJ+˳:ƕu>%9)~ww/5/WtT]%yOY})&HڪG=XO;ޗ>|?ԯ5]&SFpMth>`\,^ꏘ=h z`\,^ꏘ=h z`\,^ꏘ=h z`\,^ꏘ=h z`\,^ꏘ=h WAdէSBhuQT (((((((((((((($ARAI|3q$ i,qu_+Q%shW?ą(a zVڠb[xUb̔ *GZޣ U}{}|s!aUqf +]&Or#٧PAlB O-bVBJIGf 3G(sVC3MI3ub0kr7t~ 3Fh ֶ$$ GE:Hbc}Go`o 3-NcMуEA#­ CzM4r85("*)VM.cnoF~U3G(\oʍg4fPߕVhssu*77[њ9B6~Tno[94rmoʶsFh ۛQߕl17[su*.cnoF~U3G(\oʍg4fPߕVhssu*77[њ9B6~Tno[94rmoʶsFh ۛQߕl17[su*.cnoF~U3G(\oʓsu*.bl}DTsZ N*ķqhQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@3mw^X\c}z e_—{vYb Y[{RcDt-qM"}z6i5UzGq0Q5ZNⱥ6ͬDVBLÌ_ BY] KF+`!tu̍:Pq@"y7uwL6.$eQ^G|H3ar,w$wmFa]>$r%m]w;v}.;jzޠKq'7A s;/x4-u$6ӓ5vؖC6MڵEɌcj\j-V]L p .W6xK()!|)!FXq_ƒgjͦN;Ḁ8޴Eu2+|Gmr߉lI*!]ÿ 爧KDZkq ɁT Q烚z*ZJ3bYvmb@[~wYXvsj7;x /Crz5}A/n.4bMX'`p>lqw=m5^({{,cyws%Y\GsY1;u ^bhj2iF њux#7ɸ8IE-FMI7, aA}2HPj]_}oj]oZ@gB` :VK~Qt4U@C+P5 diZH_@C ΢̺uk݌j*<),,7RQ@[Ayn]8G*VX[,-"=RQ@Q@U{Y+Kb5 M+Pc| ?(EQ| ?(>??./QT14C:G]^] &,GVHEQ| ?(0ߏ ]^x@"`(@Ţ)((((((((((((((ή?Dƿ5Xfxsɋc"?M{E$vә"Xl }9} o7:XCRT9I5>6֗7I[| 'i+˚>Ck3eI"~7>wZ_^F2hJ6F;Q:>ItuܼgpWnq*S-tmDc"',+n쏥b[xEVh--PUP/^ua审5RĝT~(`KCW-SAAdzciqմSŢC*FA<Ƹ/W/aWUiRygDy p+,Mm,Ff9M)]3p;fxSskw֚tKp$ƌ0*<'k𷅵]Zuwl ϥ5366e̟K+QHK31u&2OO4aԫ(e#Ee oJ$Yibϴ--\*@Z5z5]-I~.'8$7x7O6,+HI-̤Ld Oc׵oZ ݴWҤxCe<pEfmeGHճ)iHƀ*@d4?GD]CSrMbX ߮{^mfNk NF$0S=Xj6%iRiD_8 )=ѢUngXH2#4~.tKX[pŨA 6cr:ezE}rdl pp}ikg5T]b5xzvo1p1 <co71GeY7rFFzzdo4{m?Sס;i>Y#suaؽ71䶲gG'W;pO4ۿln屽2r / 1l{U(< _ruo+VSuM:.uN A0Jh[^)^h^N0@=;VŲgc6.B|\sK:|ynL(rsⶬNnxSK7 ӡ&G2Y?@'L|@?glٟnEǞMqumOŐjkooȳX`w8ǚuVؼ;?>~FqKbT捦X׾-Զ׺Gk%ʫ) +z%6+K>e,r+}i֚]iP[9!eé zGmR[ZktH*R_hl-:YV8( -קnhƛiWyC'J_ [xBa7S#KH率׸[6V1qʌ&=q@vrn5bCxԴQEQEQEQEQEQEq~Z)B-QLAEPEPEPEPEPEPEPEPEPEPEPEPEPEPu%-0QJZ0hB (`XwOс;lq|TAw}4fP33 1ڀ-1q??EWe9/??EٗPNpT\1c)qq??EW̻2"cs|Qc*e/??EٗPX 'U /p?ُ\1_2"˯ ^ߨ3 5PiƱD6U.1y~(̺2O2"˯ ^ߨES̺2"ruAGe/??E4U?˯ ^ߨ?.1y~(G$!Yz2"˯ ^ߨ,q|QuAGe/??E ,E#s95&*e/??Eٗ_P 1T.1y~(̺ f].*2.쮭k3ˠVSG{P)j$Y..$e02UAbO׏Ҁ%g{?"?"bQwGQwGP(o*+%y̲\IZ)Y/҄ Z(((((((((((((((gųIo]fh$hgGBC)H ZZZb)oo;F pB={GU3]^h x?:tc]2kt"/M/Vn--nZfd1F\>⇨OHlo-vtKi{Tp7:uZo[MӴ:Np\̺O,žH=qޠt%F4]O6*Wz)`]EIPMѹo-%œ.AҐ3Wǧ6m.Sa++qNi&|@Wn/vŏkק?6mtzA0|4ϫ{vio<G_֘3'~CTOS7Ho,b-I#c/ïω5H--HZrwx-t6Ō;kq=֒Vyiܙt!O^k 3e l67hw;⺸#k= +aY2zsSƀZBHIxCF䒽M,+jq3|Glmu67{e&9U,y=z>k`/tRnJۃ>?b؂v+ 뷦iY83h\ϕt{pM#S[H+Kvr-w~]{o2jGC*4ݾضhc/>zfxK6YLiWw96{V_gOJHbEQEQEQEQEQEQEQEQEQEQEQEQEQEQEgQ+6OMB'yGP>ԆibSNki^` HXcZk ivwq;rp x:0YL"1+m޲UmFMFM--G$`g|svLsևu kdjl7WmjeXaz-ܲ hcKJes@lx8.v4Б\5gPnCm;=s =P7THL2FRH$c@4ooS\LtIoAnn^%hCZUysk.Y|0O`t;3xki#:LrRb=L[m/sz7y-+LT[^S$q2O kg uζ>py==?]*73ao{64ooS\k;] SW ch.'=YA2_xiMŞ1>Zi# 7#>}+{;溵{z7I|{S+yênscަ%𝇈 N[acbo$s=k4cF<18b%/CᾛoZ_VgiadIHtzlX-oIghfV]|`+9r -9uAtnM7ȸDǫc8K X]_i1ٍtHQ]=+nnO˪I44(BþJ3Ʋ/Y彆eS% w:(@ᶗ, =? R@Uc  c(@yڈx̆ٷG5j/iskۛn#i2çZk_hXg0$[$ &7]~ܱbH\rWҀ6|;k:r%޹iQ$죾}kS B.5 d?/ˊ NT~)7pW[RIʎh#oZ:=[Bڷ"o2 \Q^'5-rtdm.u%W;N  zG[Suc<0xЩ= CR^Hv}13=CcMмCicqpI)@'Ҟ5_Vxo377oڧVt{,0 :B; Ө}\yǾj?#k'tiFGFF:R#+5qn"xs篥CZXn`FfFSثw^I]RhecE1BmwLp(@ߏGF;qDцPNfc5j VΆZ3,p޹x;7і)"WX0'[֑.>{̌|[$?UԴ8bo7P2@|k߈6֞a"jDVrԶ? M< Jf6倧P?(*ni6Ź:xӧa $O={X 6Gqw5wSxY%`>fgֶ~EἼ3:^Ro.[[Tkg{5v,V9&WT SGh h,1G҂Y3XR,s朒9FV;K(u{Y6ʱ+y`(7|+™7 B=FcE5yggh,ˌzRᦝquo$wS6_d+tcmC+L!2v$4h-MjJZJQEQEQEQEQEQEQEQEQEQEQEQEQEQEq~Z)B-QLAEPEPEPEPEPEPEPEPEPEPEPEPEPEPq1HaEb ( )UI8 ( (j+KvIUb%?7? , (OR=H{@?-cfUdJ4\ܐ@Qs}i?EpzM4.dahNf(OI4w% "A[bS~ ?S~ 5bSS~?f]jw1Q}~Ϸo6?ʜ|N2L3%Q{ojQ:=1,R>|dZ/mME-%1Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@h_gV}PESQEQEQEQEQEQEQEQEQEQEQEQEQEQE^U[+f70k 6P@#cZ-g{xZu!ZmPGͪ[\E.vӷ5j]ubiF[ہz׮+A7-`v,nϮqֈ"i+X0cw׎iNJ-׬;Y&}FXUr(=q~N^%PIki ,RA{yN3af`XMNRo nM(WLgmuVk{{kqB\=MiOOe5ŠŶa,{WB@;4XY =2Zރ@#[kðVD8a{V.qzֻ5߆t7]om$Klpg񥵔7Msc=̲G€T.pGV5wPmc(]=qGyZ s7g8$^1 Ɔڍݤjv2Db& aێk&:h5Ye+pGZSm&#h=qԟFHl!121 oU c<@xQVos\MŝY%`,T#/u<#HMxᷩPºi_޶gӯ$WZTS}fo `Ѣ9nOjjdi'c9w늵Hd CZm3ĚVzU-ڧZC`q=kK˃qc,I\:t}ݦmE=q~ In%H9}jImmJD6H韛88:ȳgeռ[ƌFayֽo*Qj\E/㧵VG{x&ʅ p=\~t*qkV^$xom5Cx sb\WQ:z*Wɝ|ʌ`<;chmy$w\w6K ¨~mi6VSǴSq{9vknޑtg[l0sukWzHktiC(@Ţ)((((((((((((((4R1 iyΐQMtno:uGW;kA kT!+ǜNNyutQg)}_7VE.nu\1.OStpC 7(#<`զY2]ckP1hQEQEQEQEQEQEQEQEQEQEQEQEQEQEgJZ) J)hJ)hJ)hJ)hJ)hJ)hJ)hJ)hJ)hJ)m:@105f7[oX[H w y9z {Gev"YsF:sW?"E5K —84k յ+˓{yo^[FtFGRZ>mo#oK4 .sVph{q t,nlY`$~5d;?cKhmepqu`]b(=PG=*.+AF*=*O0{~T\ Cߕ.Qqؽ1T|QoʋأG)V)!XmD:7eo uA2 {]$ny؉ƥG(&hM6[#`?f4jƊ.r_'R{ 2H'q=IL_CڴtmKl#` իia$B8eAjgo%1io klvƷ.4Ox̒qǵiz>#MI+\$1Z #=k}'NsRuslTZV^,v˶0 9{ i&`3,͜1TJ|aGyL[i7E#M2MHE,@ ۈ^Чi{dHBьޝcڒVrI]1w#V\h$$) gpNkuo<|Oӛ!#MfLDeڰbXq}7}=oLM>MF8&XCyDHpFsZv~j12VWyo MN-n H#'9ZѴG=+'vсQ~y^MמbʽÃ5`KҤksF‚>ZZY\]k[Lc=x*8-)zKbXH` T]5klm,]ED^Iu=WVzv[yvt]#PF S{WYm2 ~cWtbbԬ!$0 񡁐%eiokcswj3l*p@fh6/ӖM/ml"mрqq׶+L-'R[XL!x?E R;1o5Y>(G5k6ۻlcV!Np*躖fAv7YNf1VZ&1('kv;dnQ'444!us~1oxKԤJYgKu&\\ox#[4%R$PF3'=j4+KKkm=#2  W}:F6e[]KRI$ӸX(*GE22y\,^(*GE22y\,^(*GE22y\,^(*GE23ިy4c?+ROzLB3Յt'J?ʮ;# &y#kȯuzd-{'g?Je2+ k⹒8?tTiMQj4-Ο(sH96h$/Y}6oӈ| {]npge8+&hM9a2XcMg^ߤz6EVw Z"zL\_OJ%ҮNI<^?388Ӗmxϥ?9%X4m9$r8Cҭ!E[YZxF]^K] H6 $_ov;wrw-Ifk`R^[/ rNAس]Fb+QRSKšq[M{"ɶYNRܱgSVZ,Iب3n^Mwe"F'(m}.iEA`X||{拊696^?:7_ӥ;m}ZZ,I%( U? x:sK6E%\[:FȻBĐ4gΦ|?nl7d`ATv^~m4J Sc<~pŽդ[XH^&#'?Q]OX6Ț]vC!LI7ޝbGT|zoo <ʣp{̣̪>oo <ʣp{̣̪>oo WzR&mpNjUUx^KE*/ȣʏy+>}G-}R5-20z<+oZ6v~V%_zN&?ȣʏy*Bi|cg3.}Psxo#kȎJIIQa_<yQ4ERt:tPr,X ~)qu-g$̥`ʋy(ci|F#Gaܺ/=+6}߉aҬd7y)*8*@vCG<y14Ef=X.'Vhm:ΤG>c9ĺ"Gi' K?G=h~T_?GOVgt-<*A*L?axǐhmsonEvD2K|{J@vT_?GOV{}Go:FZaHq֭kVhgUT-a`E<yQ4E,lr2 gůirjvn^eY[_i|<"YEL֚ yVؽwfk^1,|=wXOoGlѫ/'u;yQ4ET?\ -mRc pHL֨tӨ-4o`˞T_?GOW=u=5;J nxd1NTg?ҮsZ]j {K-f8`4X.jyQ4ET_?NoOQE<( i|<"EqT_?GOS.7ʋy(i|uQ4ET_?N*/ȣʋy)PE<yQ4E:,1&B#@s (銥B3ʋy(i|PZ}MMp3ҹ[Ouƥrlg[$ɱ e\uOQE<iΊ́6Ȝ edT|i_Cms42i|<"ƞm2MAumRqлw-5+MON\ۢdeNW qK`/Q4EL?+3EYYYGI- |7V`S՞[!0Sv wOQE<mim7eA>Da E<"*/ȮC~9MRL[EQ },QAz 2AsZ,öv nnqڅ7t==)W6<"*/Ȯ Ǝٵ#736=.5:^5ƘsxQŹF倡+οʋy(i|yc|"*/ȧQ@\oOQE<( i|<"E8U9P_^3Z-?Qo5M;^[Vt]:W@]fMo cZfNi_oV$S@BsXeOjįÂ2瞄WE`(U-M wwX]Ud)Ծk7Ok4WsY]^U93ެj ém%A0#$pFkӨ[?MI$o\\u(yt3Y 汸xKe0FGUIpEnM؞r'ך(M7}fKif]9tpa!+5sSss<ڤٱ$"q?(uOA%ݝ7HPMsڏn/2YbXQP#U)~OzJ(TڮtR@Vh?sAG)e(ʏq^EgiZM+Pѣ$^q|4Ԭ .,dTa4 ˷<(|Xᬧh |mzy^[Ni෌+8 jǙ-VZfếԤVXXw{~4w~"e%4,t  9J( ixUӵmIJk]6yc'\7|ϯ]?O_p";qp3IE EPEPEPEPEPEPEPEPZZp*?SW1ڕt] 4[?dڪKi7M[~7{z-G4Á4ǞHgwyx{qӏamO+۫"6~ac/?ZgcfCT`mfvjli_DykcrjM5jmB3΀g8Oxli|23I;Xc;wZk_[ϴ'4Wg1$hVKsޭ 9ꖧ/RwԊrhP/?73ڻOI}/i?9{6sxb[}Jcw1$|몧}h[ϴ=9_}J{mQ-$9Pml5ZOCLO7q> p9 Rpޞ54%5Z1ۗpgk@vpz˯j~5[MU6R0yz(# cƱGįXx Wgk; w-IhS7qCC53ͽyueY@$@Ey6֚;&v.74J9G]_MY~˱u? M-W!?~({GsXIU8?Z]yqᅳT)g"$n8_p=>i~%Mm mu"0\M'AU$_%xtf[EˁI&=*7ڞ?!yծ G: z.yUپ#b%]KMőX!u*qqjj+̇ۆүIKalDf?$]^qQ_]j61]YWsNs)RREr'ZmS]A, t=k&wZFjVp|6:z6_.uM4ay&)Asv$c`xoƚY=Rȍ5̒*u…;y1@Epv75 SOтŏ%D6*yS$4K\1nBh\+4WFZ[&ڔFQ*[Vot{Ķidh/kktYi4RX0VF]nj}j0O^~gXp3C̫<+viK,m#H>}n9#&_U緆8K\p#[Xti˖Ki%lXk%5usNӐ?0++e$S͂6YPe "A$ߊ.S#c6qr N焫d:/ȭ|y,rXLҬCpX3F3P}]Vk_f,.6x;;qhbJʌ_ᶓRC  $=ZnR@]`xwFQ2FTc+ɸ񏙉=k~6DĢZJ(J( ( ( )i(((((((H?֯֯U?֯֯SiFA#֔Ro  .E(SJ]G:}-枒\Q+IH(#gG>flT3 |}":':zWfÏ8\ T𾊗6 b5bdpQ^0Oeki.Al Ļ2H9N+1e*]#i칅-UfPѧ\կkIkQJ}h^RfhI1fF\)JԢ|->dl0}IA_iH8(tqph~oxz}DͥfE3Fpq-]ńr ĤdǦ+jv-j403u.IhRmkZF2KZyh̎\.|)ulM̋4-˨#nWt ].d?g"t[Ehn$rO'4º΋[g9*UCĐ4?OnLL233naѲNA|?ٟp/4Ncg[9'kG*!t}_?.tE)<+Ii\= `r^9ei}symKU(qMЛ8ʿ3,>?G:f?oWo#w$ʀE@==C҅$I\KM(`OaTA_ʇU`j1K oxODr[A$On#kZ:]͜q-& !g| ?(>?uѶ8|)CB-=rq'nv)xGBNOCmlšZ2z֯} ::<"; Kc(>̎#^j>a-9IC l.p3U@C!t}:@9@`lm# Dz }j> 63Y٦`̅I9\{b[PFN/. ?@ iVZE[y1o#{1'Ԗ$T~?uк+Z!UAI ֧֯??JLF}_QiNkVnLc2(l'ZbluM5ɧX ~h֦Ey|mXhŬ$ uIY(HI'-R&;Hȕ|u рlqNIǵ<~ ϬxL!IO2DRx^#5hڛib7FxK۱sKaǷ^{\xYbR Um!A"QC|Aͽo# :dnɇ3=#ߥyk׌M2AYv'ڡOZHlmN8!#zd.Yd"ǭy-E.N:e# q(tȮX,iW#Tռ%vwcPfǩ!6|YY.RdẊsp? sx1۶mXP10#:.a \M4 V0j:v/$'cp<Î01ҠUkӬNl$"V0q.z=-yݯu -&;k Q]jIB, VGY w3&XI#;AlvJc*xixJ֭PQ@Q@Q@Q@A~z~zIJi) _&iAPQ e8;iRj-ŷڿd)7 @=yOj2gF3\E߳j٤>1Ѐ߹nm{mjFFB|pJO+;EMqd-`nx9?wQ#Kjo("&`Ѓ76]vK{}V_? o!~p܃u@-~R[w۠*W#>P~!i:Ter]FU #o̷꼹<kMN8O*kI57z)oPciqݴWn1Hr8?YR5f("8(c 3eX/P ncd]& ϿOLVػq(\GL|jwO%U^ Zri\+uM1ET.nhky6xz|q; qh?xv{\\V+g\z|:m:q'<$z.Mݬ70L"PFEKek CƃSm{B#LA>b/'P WOUQ8.m"2MуtmgҥW/%.2M>(#vF@?OXxkJkHkki f` q]&?^ѯHnR7sP OW"($ 3 篥X]gdB-}:"y#!N?^y?VOB tǮ*mnMf7Ļ"Q@jToUvPҧKI]kO=u9a7Ao-4'Ibu\c$-zDe9"r G_sVot}"uf!k+dU nr+։pYfa$Wr{iSSH{oe y `{ʪi(u[5 Pά ׺@v΀,Em%@08UO>qTz嶝g-Vр^YX*N9'$Ug?qEIsty!UWX` A NnX.ztV^RKͧDP &s~un Rx#3(mmҭyRxkE-#[iLr0[==k^Ɠ$k&@5RKg9ɨ:fdf1^n@Ap[ƱDQvZ((((( ZZT`ZZLFq4RQEehힳsyūCT,:=@Sfq/#j)c@3U4MNZ m8,i$t(i]$.UspsEIePdRKIJi(k|?5p ƌp 1>2eBrQݢR-"IjIo^zYN"E7ۑӸ#]:: uQ%Q]'RQ@\"22m# ON"in`KV<+P u ?Sӯ$䅆N\)r>f>$_M/aD.w61 ('OaZ}g HAw$o_Mh}2;+mCAG*fRl?Ȑ4} H C| >m>T\;1/&o"E߱[yuO3L[HV HU3 \'cDHoy3$fpN8pAs F)Y8aꨶ[ n8fe^*ǏG*f]tY#duܬ0GTk`;?Z,qӵfdn G_.imt1ۡ?GۢDk>GU4LS g_ƙubJ2FA7!i $:CC| dVFpz77!kC6( C| kX`\H_EAW&Ǽ=GZ6(?o"Cv@7t)@3NF3XJ!Mr->[`H+9 (֧֮?JHlȾK{,qc2^}xUt^K/ -T H$W\DdRa{/k]>)渾/g{O6HÐ44:WP\ç-IU@',k[;Qh.D\ND ,=F?*#Y ̱aw/P7Vq0w!S q%l.7P``i庒x\|_]{_Տ7_f7sOzXp N}MF6[} ݏ횷ZYkB1͔['%OP$_(3g$|ӼXIR]1ӭ䶈;$۰' -3+/5=gK[|Trs]ΕxkF&PXGHr/C.SWKE/`#P.Dҭt-g]7\:bW![]m⣍\|WRm5kk-ʇq9t9/^/4MAS\t7\M'c 4˷dl0NO^­ .[TA#+Ih8.=B)554iJ4 B#:ItߐxgeE`G'#ONmb4; +Jiͨ/ŕgp2T\i{NΉ-ijN2| u?;;y,4{y|2}͸8=*Ǔ[k^m,f8mLhF\zuMt ￵~D>Qh'j;_Z[C,zfie2e:`=΁&%p*䀮ǃHkB5xwM6V-3Hҳ@ (((H?֯֯U?֯֯ShR:+ ^9t "4Q#"8{ױizwy F֫qf'|˿ x7gHm{P9 C+ڨ#:|%$R39I(@&L=;%yZ尜E[GkѺzj(`VӄN9H$݁֬QEPZ \(O nUV\E#nںRR0Aj*G.&UyX5ڴ˞Tc'V<]VEyP s]sxFft8{d І(b4%Q}jxiFjrgmld'Iӌ^[(8(!>+& NU^I$L`g䃚Y9(]RTlh 뗚k^_-@ܧ`+}}cmXcO\21UoϬ-g?e9]hYz꺔ׇ_ol%P82}ɭOF6Q˗o3In:{?hm?r16_YʑZZ❊+FXn?/<8j3KoxonL  'K_B7?j%ϟ`!x? mXcL{d?e9]G}tQZG5+ y<GQqg~]#P[ooXGRuys>ƏϬ ݟbZtֆJ%L:tҙ%Ӯ>P{Om|%KkW:[rjG 8]_^3OeXcP5&8b7tb?ҩEO]~gU5[acM' `g)M>KHj|4Q\vfm>[ȒUUv >U-kֺ-^,rwڪۇ,[9f޳cbgPq(?qSWZqbIt%JcQм)wF 2Oi7_߱U/ u>}>*W7+?ti¹c:Οy*L|b_߱QcY&T%0P;u5^-SNE-BIU'RI48:*u[ݿΐd8w3PI'`ӡvaj('kZ0Z-R^Ie>9ؼ4t`:]6) }y1%*AH[FFD8>ʟߺfKivLzW:13I4}4+INz^C:Sk/s N[j037wv6j8|_M=Qpj6biDZ '_G (o,&]V/o# \b_hIR҈YC/7g-c vJ OGuu}4PY[^} 32>P3lWU_[jVqYI@u(I;Ȓ*|`IEPKIE-PQEQEQEQEIb3) )R@QԓYZݝ+c[Esm?,I0ݐG\Zcygh`c9(vhcn~8ETjWKZ|UտKm7>Lϖ`|iN@+^ mQ6Ng28u³o6iZ X~lBv8޶/5_BP]WϯA$tS6uBmB4>uZ#'YD68)_K'CF_%%}}WZjyAwȚt4zpUUw5aI&$2rozN}M}) ?:[i/A4 kdo wŤ&>g/6` q~|`u^[qf=@[JuLF5S[Wiο?Q궮/Ɲ9կu6R9o$h s,Botyv8ڠh;KaN?]HWd2 (zΓ]jwAqyjA]nI? ?ONⱥ*b .X`I@91I5zen:X}*O'$)\,_tCHbYa".V"0e ɂ1(00*Tk 5$Ku"mWI~:vP'SOI~),Rk]`r?HikLS@xZֻqN\@` uX~g__+YMt. V05mM¢ H͸ej(`<w5Σ-[|opq=OZ$jSèwz b> ?*h K݂l0 ]G}Kf999$vZQpQKI@b(((((( ZZT`ZZLFqIHcV(WcA+Œ2jQ([[p%XkxRq NQT@O"ҟ&<1!2֥o~י QبNT[FSЕ(# 9QQS@4rxL#{-P=_Nլ$R>ЃW)R@II՘w 9Ckt[]Ѡs,4:>?Ʈ,sDB$l2YB8joSZqvEV8QE 7 cFv)K2K:*}xHo."4dyghe? ?#Y(߬ϥ7@?V_𿑦GهqG~>E"˫`-,So(e? ?#Y(߬ϥ7@6~v|t>I}G$l6EP5}~gg>߬ϥ7@PDUI* -֢DG @PoYSk0Ω|ە8?\~t%m-,G?ҥO˼ T#"q2>v$EUCGcy{,/"ЬVk bR1Vc#4E @o?~>E4#LQ/'^'jc},)C.A>[iiYFJ~5XK? o"YSzmFǁ:;J~5ꤛK>E.c},)]K:7x9rC\wg%2M+ Ao,9sڤ:l̓}A2#A~WA}nNC$2: 9qKa񭌳oW ;m#\Z'H5w 'LV# e2:|K6Sr5ݬȖph7vZ~jsL$e y #"=}7A{݉˂GCn0~޻ŬhV1T6z LU;)}x͟M;;h9OIv⻿Zcwe̩gm5ǤxKm>.ܩyv>VweR!ss^vDcR&mCxIog$6^QhS :wgj<4F{g+fnn/ʆ$ X<jS2=ڶ@Qە8 J&P. f)Rnsb+^gK}ZۛOI|4tvp~O=ּ\kq'*[HTs*uFJy!/L_wYxKΥk=dnMtIq/0gVN>RN]FyJ7G ۧzy⣴fոniSHA&⾎9`r0O ߡ>ZUVQq|Vkh:dOמ?liz ZЩ;hP@:SmSn&k$VՈ$| Ws^éi׶Ę.#!#wnZ]D\_s1n${={WCi:UHF#VK_-D$$*[mi9rU}jCw:Ǹq ERF\m.sԏSHduw OՊ [ͥެP]Gi4R=#<k˸eÏc+=hWgoڙ 9 ? E¢Z^gĪ_<},tQEzQEWE[-q#2CZ2=*%Ю4&Q! t\r~uyT.4fڪOXnw Ϋd]@ː9kvsN{hހ!d?47{/McXմh6q=5&CW<{\h4SX\\mQqu.}4}5}nTYՄLHH q]Vv uc{.?7NkR5ެۭj+{!k-FBfS= oM7MVW\7|ZV7 yl,SDd({W3@Ͻo&&] s⧋f _IVH#%9ʃ3Ǯ$vZhoK:z) ,i`ɞ0 պ*j!Er?u[EM=A+kw@ʯ8!Cp(Qf\{/Mmm棁t?@\i jZZck> =4Kh4u,ҖમFX)$v#5uXtM ́sG\e=!.8fU*?) 9ZvW4Š6cC5r"4O{ <dNDe˃Ёs@4VrC_}uK#w::3S[ҒK7m0Y3.dg#Erv^:5-.)}ӃSP,կ?2U=[#^XmkĿk)4T3~sz棋gdى燍; ((D @wu sQv??1\p[ *+?/_jlLpn sg@}SW_G۬=5?uXdQN#u~_i8º,0|P۴MK]nVxPG"Juu2v8Գ\— ?֨UEoI[—ڤ|PiG+|r!pY1NvI zj_*K{5@l>вO;(|Eoj'XAw l\a@.>T\Zp(1R{YZEd(6jizT m ֧-FIUO4V<$QF(I5xDX]\[ˆuvp H=УdYFr@ ~U$>T,דL.39y'JKNs;J{iP\n{̙ ڭxKq*w['5FTl.&w{Mw `c#@sK?k; ȱ́t[iZ u #GBUݎخ˻-n9.+Hֲ@`,񎤞`3P)h뺆<J"$֍& ۮ=gB. 2X"+xQ4ejڍ0i>Ӯh' ^bZ7C8jKMo  ${S#z(&.4^j?;#ԅhxs^+9y Kc9}"* $>_jiӤ3 ,젱9=M3a9 䚚Y Q(y!VѨ_i]U6ϗnSS5u}ln  7iv}iA$3p[dzTtRXk:o:-S0wi=L|=B/Fwb=7|mg>ɨ[I3Сv8eSѶvWЙl" awXiPLU '&3t XzMysx^eaUp?*n9k>]{HOMN,8vB?ѳVQ-n.#"HH pr:>Iң+Jk[t{uwCeylup.#igr[[?dOG!!嗇K#;q'4dF2XV??ʳ>ƐBA>5pK$>>ƕkbt?YeOH- _p5kyigt*õ6G~k'I_h$3Fv#&:<>~*D _qE!O!y ;2bck[0ۖ @<{u׈n{ cWTMp|{\LXykK,NzV_/PrMi=kfUv½2KOjVY},%s#8wHX~d{E}Ui_XK |H(L<( {_^hwrb-Ǿ+ШW1l$S$Ažz*gxS$C?]_iBWn]ʡ 2ǿGOixE1\uǧ5\M:T:QbQEpQ@D?u?cZ5RlHE{KeO6@smC8u2DZD99S 0#;}[~6ؕ7oj~}? r2~-;Ɔڎ5*? rZY-coLJռ^[OGI-DӴ;3kYin\c`n8e[3<fsZu?UؖMSCiq6t#8k_uh1)wŷ7 WM֟YY[ is$9Ĭ>uv[˟:~-S'i2JWnvg8h-TgOB 7=:RڛPP0rTҖXX78M(Uydaa tqr1yFh <|7&y^y0Bs<ŷ7}[3w&ƆڎE{e5O}[3FkTo=ށhZfkeb 6MRo4"AQ$jAe[3<frqΡMVk-F.-ewt*мIy44%`CJ_M?Ʀ$[D0lw;toտSЭo4{y.5>.!1݈q'5k?rY^q.ӵneYI'Pɫ$Y3C ? o7I.zɡkvFkyɖXТ"2t68϶jDu5t9Vu+G} wi.͏s D<3(pv&ԎqصL#2Jmz*(NW_N<1 37!cY@n[Ύ%ⓦO^O- ܸ U g4 ?Kcj"5{inzZyqX>i/ũY_-ć|1HR8'֏kolmZ&c"64 u٢ h|K5{Zߗ=pM'AW_'4ՑBe@?Jԣ4_K ))i(QEQEQEQE$WWWW4—|HRKm+/Gf`^#qt3ѮuWB-KZ!auՀ/WZ-5ʹ^d,sE svW=j!9 lQ0m;t5&+ƭu}"Q+.όֺφ25d x$.|sHT-AwRJZVtY iBf=k^\[/##5Y8nmH[6rCΠ<= k9?w׭<9,omG<۶20{W < 5Zeڶ2\ի7T֞BLYK{֩9'+xz>ԥw=(H( 7BFquV+v梸 I( @O5v!%=Z[h."5Ʃ L <*0{~3GP;/|:gE֜yݍ=;fO>Q~д udc? 3|h?/ֳ7,Nm-B`~f9 Ƶ6/t{h"I$lT  ָNxwS}/VIcJnN#p$q;\[<4R te?6qa g<HVVU?^V%GEo%Cl/+=@ҵGN_:Sx.Z]#J{Te帓&տ/B Qyhq$bX0n}H@[Euc YO2NA^S$_tBk .VKWrrCm;$t&t1_$A"Īێ}Y=3|iiŜ\_C[;iSHfg>Q~`N izFLdI.ۇrAϵgz&e,ڟԭm$pw(nm L gQO?/ִ_ݟu_WӦOk eIJ#Z˵n.ඊI\G:sE&[r]XA,XG|A~YCtM6>5rA1z?My\KT\Zp) z6M+]E &;Nam;Νlmz<0FA1?t yE0woOlRA؈z6s-żs쪩#8R׼q]߭r_ԡ,H5u U$+o]}k72q%&XRHdP j]Wޣ]Gt$4rFRp;]JY!{wh܍C*"]LD%6֪qc` 21Xq9m>Wާk<98'5s&}h6B;X#0R$1' magV[˛cy"*+ 2H%_UW/ {pv<#"c"4,ܜpֳKHC;GH6Ѷ+ʿO"IbU */ȿA@[ĸnbxS di wXJT4.BUHKhG6/[VRFņ3ZrDv*$s\M5HgH7qK+ӐǧZ!(QX,u=H]^^&Ynt;qi-)H^m5`qVzc;TK Dɥ[*씫mŒ篨ς4K--4i @"w l>ⶼ ]ɮ3 9%F2yi-7[+h. ޙAw B *e9*s%f.[fUL+ts''˓ιn'Hk&"9E1Jy.jI.K˵2$4RB~9Kkm:GKx6"4@xz]ލ.xK($+2 ulo-5T7Wi_=K9\}hֺays#nbI$FQ@Š((((((( ZZT`ZZLFq4RKXlQoe0p\SKӬt;cA'$z;Q@hQ@%-%-EumݻuM 285-A}ymai%o!'kj8nbh9ȱa.%5lim8!;Pu>?ZexHj7Z͏-Vl֗u͹8܇#>ƲkU(9Š(IWj2ʬhw?To%Cmie.!Tc)>ũgE9IQNw?UC:GIhC$⩉|CM=UO:VQ,E>S9IRhӝUPA$q4}Q8n%6=ީ??O*Ǩ{}Q8N?Uɒ{ߴIT 'APQq4\z֗9椈6E!.3[I33qS-nHH/g5c; zΫ"kb'q@3@j}.*C.$?:Q8^;?lĴdY['#O:ZQ E>cS@js'KNw?UCZ}I?a$?ƀ-|Y0-.s_Os,\hZwu ' P?sU^[[j <{Ta$?ơ}Fy#z{@s2zp*W_!>^YKwx- }$t_{Z&0J,g4QL#bd46뻦3@~UZ2NO#&OtaycVel.mm%p'92~e2~e7PK?y3ڮM*83lyqMA,0ěK3}QImCW?$kx M>W?HӔ R@3&{}Uow. M4’Š(((((((( ZZT`ZZLFqE!j>ub}>p]5*szs"k ٰ&=贊Y[+{b4P}w2I\19$ hcO{ch$h/Ege=ƘͧYg C'ޝUm>#\<>]F`Is<nuZѷٿu5nyz  0H^*!"I]>+i'Tm  fZL"%rŔG=wAӒ,[ZA jm"}G{W=[iWWmu0yyg769> gckcmgn<QArkbq$H zɹƉr4e(#:l NHZ>-F%𐌱Uk3Nw4.K9ݐ1 UCYUWcl<*FOUݤ+ay^Z׃թmУ= QDP ` 4juRæYuq:a)'Vo>y-uڣA wqnR<~ .5CdX6Xex n/}F; Ok(yjy7ksE&𾬾73o;\\pPw{*TЂCΌd s=3^FhZiiu. /yXL4Lrg;NE$yuT-[!xx 8d2LȺ#Yrrwn{K- kD+ؤefUa~I|cG7n(8'MV:^3(zmSºu֟[[[j3տtQQR7ns}3^Y?6u/$bx]d "qNG>Y%\qZ?cW 'QY?€8xNAִkx!a*gB8Ieh<OF?yjyyY?S7n=ެ1qY=ᇇ{kḱ嬙Oj'ٗC&hI"itg <{TN\r.IZ<6-!_2y呶{9^饶mck+i[JFq5C˕v'C3R;(8({v'  UD|* ?ݷ k |-$R|ϰ[n@'7&$ [̉i] 3O1~t!TyI hdUx!@tΛ/*kPS-9\GoB ihZΝY;`3$yaG?Q@wcl?$ Zu?]ߝymmd |P:oM&dP7\p{pzU?xM55qv:sWȰHp c+;o(q*U3Xgۛ?H.KO,:4Sf$ߜ_U'7&_Ua/g@͞Z"c>Ld:`zV.B>HgcN߀7& d?U~xMco/*:oMkzfkγy?&p[`j/wejWL М: y@&| 7[1PjM׺oiI_p3@t!T| eE֪(vUIRq\$iMYTpi2^qj΍w섏€,>uU(/֮?JHl΢:.Ɠs3u<(bE7s37??QMb<(fyPn1Fg)yQ>fgn1@yPn1Fgњfgn1@yP1Fg)yQ:ngn1@yP1Fg)yQ:ngn1@yP_^S#b3!$\;C+ļ\*׎e(m_Ma#}{bq?Z(4^ixZJ8,aWX.ARy#H%lS<03/r͞5<si,Zsv--TH#O7佷SUfSoT һ(#WAfg Gjp訢-`:LχdYItT*iŗNnC`Usctww8t4KcIs arTq]m0l髍JnIQEqQ@B?sg?^]wRS^[k$E9'> (wr$߷oT`Q奺[[Co|QsW;aA"/ְScQƿ`kX~? )kI(kx@[1b `oο?QU28ڦ_RkX~? (}Ðizz$mBui+j?jvL~&?l??E,Z^8 ~/ Dր4c\Zp*_SW_gRQHbIE-PIE-PIE-PIE-PIE-PIE-PIE-PIE-PX*rMDq*+jJ0,xS5#:莢'?z46V\O%vʒU˜H l^Ϡ^Ϡko}]ZVcwHgs*ʮpxxNthLè*%[5+\z?$Сy UbG{hỌ^kKN{sYUb h̩՝fsIJh$i@!Ԇ8NRIp.YѶ(lϸd^ &I."Ő :gdbød`/d#҉Hڷe 9 njf.4mo`Y2==9]VW]Ca>m-LhLvl0ZXsq]ry/wqԊΫj ͰR-yݷ|ewt[I4jƶ}:פx\ྷ>׶2#<{\umei,c'O*0QSП򭟄V R"Tyyi(s67EAFyEQEJxXdDma M{[Ѽ;Ej80PfA?Y#N-pw2wpϟ)#x1l,'u4_ʏ-:%[xI3cjZ`CGo_;{ 21P<k|ZNS^&ܬFCk[In匹u+ds:P-<wޫvV{\LPX'׊r|&"ѯZ7g,vEtbG*v rp9HomSIJeF:mT‹ݙŒUo 6,Iz2ΓܴPIdէz?ukm.\.66qhL4sx# ̱*5ntq*_ݮdחFQ!Y2r1A3NkKy]r}nҴZ+:X-eIpϥyh~_V jq+)*Bkoh>KmH"]K46{⫹=_/L4ȿ{՜S$eb,T0#_:Y$Éx5xSi$կ.Ԓk3o5UWo|ݽח$ן_]~J?Ґ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( Ttdʲ_G}*+W,p #?ّ"%H@A@xQ]FBÕО2֣ws{du\c* ֠V9uLY$(`0F=koEa{>ֿnb?Z?jL kk_o7M[~kz<4_S54xH ֡p?ҟ{ߵϵ7wk 4_Sر֡O4 3w٭zc>Ӿ{>ֿnc^.oO$v:s]UZC,rgdݳbg 3zztH^;V .q6%Ծֿn79#yohPk CJB}}uS/?)ƏXPYf|7s}}!RGS"PG?֝j@jk_o7PܛDHݱjz]"Ex귌J#~( (!g[BZ:دWLxZ)`I =8ר90Cq峠FF2Fps*ɿaYs(fFgP7ꤎT6Iho'S,b*/om~$ bj'n?q' =f/61nt|Vp3\&$xK /7$uMc$9*66I&?qcOfWM|j[s)vp={Wov =~!-H <m ~Ǯ R[X>-l2Ăql^ kV7Zж&?UVss,7X YXͧ:\I;@_?xi ZZ {m"fKI|m!1֧6&I-}%968-AƖ]/,IY̮ ǜq֠>'_"1,YO/<2Pf=>&[ YFplK??9|_ZLm.$,82qx.uW,b֥D@(#<G^=ԏ*/Ϩ<®ƈ w#FyQd+3~ (bxV`s F~`[Vp-H ڢ"cJ0P_#նhVH2"P2Iqi"|z*S]wB[}8 +(bg9r6;8*tҟ5A+( 2$PD$Lݚ?v~/R[k촉#{9fUI=fNj,I$Nr=b/W5rXb8?L)KB2_?7,?7ϋMgxM2j?:-s7R7[μ28㯸CVh!ܥ/*!Ųy n5u\ayz8~oZݟu[Wӧ5ߔmc%||΀*yzj% 3D+!/_xX|@{P٩EJs 滽ys^2h_WWQrjl(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEr.5/^MjQYKT`Cpx8hc 74"fRO/q]3nޫ}h[xT.$}kLD:ۄ3 f%tPtA`!LX$Bpgv}G&fc,h.Wkpw]S*x[H%5+(dbP9rg~ՂWnO?j=,J#cR4TEUT`)Ms?i+(M^e>W'R Xg<JtMyg6_yQ/y(?Aizm݅f)*lsWK/y?‹Yegb@Np0?Q[H>]m̘Mfǽr`ke*UA ?u=J QH쨌@@2I7{8P="ao<$`cr=>F@G4l4|hڹ$$5/y(M^_[[)UKyca# 4KI4{;[˖ LKhWRyX\"ǼJԌժ ,LEhqS z%VJJw#PdϧUagʲm `_֘C (+l[9QY^/|^u7m ųj$~+?k/'cX]a `qPh2FcU}?m2VxXLy/ PtYFIXwTVywEoÌdUCšy%ޡW2c|[#``d VM,\aN~@cY~(m i`3o$0FgHC[;? xIk;7Hmg3B89-:[OGepq›ru o|:cΛoi qKA $ ryW'6\-XLTtn{%s@H3ˏo*}~HLkt +\SsӚ]6gAl'V$[ZFg=glkZcܲH0k6FHMsy]_<|7 iiQ]NaO=Tr/SV6$_S@vQ/.aF;CK @OMCkiso'ef# I~C^R duWM𾅥 7F. K #z_~G\M-5̱瑂RN?Q6\Hx@9@SWլ|HͥA onPrqkW G$r{r+ÿ+\tܤA-6T /s Mma 2u"TT<Ƕ?P`i +4't@i`j~c?U #_—H]_ 팸7)??*]׿쪤#LG_QO?e~W(Tbpr E@z}onmlA-C9.XIuaM\y5ZZjWaEP ((((((((((((((((((((8RJCWKKjL&KXH+W掷#ȠѵQK,cҳXՓ^wx[{wq;8$G^UfPXVH?yCu]R/rơ$˽цی楼cxťjpE\~/V ֒\8̞ u[!xdXd狏sZ3u[qP[@ 6AO^DW*q?XBXC38@ /.>?_];IYQ?y/ >?_]1/ \#@׆-CR/,e256Lh/5;NC!:(`/X{υG|.憎Z>&h:EN3/mX\ r O7w.q~/.o|.憎}U2[Bʪr>z?uk^QO^/.ןhac1۰V/ OycמmUa9B/>HLԫ{2YH&9JVEY_5׺uI`ѥe{C ~iwWbPy 3eTvυG|.憎w̛l1Q>xMW{υM{ FE?4^ށxK-#U_y)f#i9*GNڞR aG_A|.憎>s}Eݟu_VNk)d- NA iHɁas~ /.<mSOs—>^$ |%G$ןs}E{@-( :/֯3)ƐŠ( ((((((((((((((((((((8R!K^om-i=KV}"0$ 3.ϷZ5ZQ".<=IWh84-eqsnq 1#t/ĚmL_#ITWMi -̳u9V2 `>8D/Z]W܀9怹rw]w,6񌳷j\gŨľTbB5A85I8EЂR1}H GnlWB֬5w O0šV]-p?[ַK,O>GˌJ%D= Vi[iw=( (*^=Ckh MٷW/&g[[Q]1[4R4FrɠDͼͼ+yR0I:ARbdf^O,^8S?ݭS#_KB2o?/s~bhͼΥ}JX^.ݓɨ8ڵ4 Nɭii[Gp&p0w`{@gy>o?/s~bkOoָWշuT4خ,c=Ѻ稠 ~0[5iw"L/5$ @ g|>nKQiY–И6H~x1ڟvC ͵QxP%"SxV(ېiڸΝ7B+?ӧ+a42ғvWٷ14f^כZ뷷5żz_^8 = ^yxF)DXv*+}~VՓ} ߘkk|b'ճ"C2o?/s~bio?/s~bi!խ-#}MQ/[U$z>&=c6eI3G~& ぷh@r5{wm_MjN?Q꾯-t閉wxypVhFƪ 0JZPk:twE zRi5fT[N<<bPm=5I2 hRRho4!HHB$0VyRSIa;MpK2iԻ鿕 3<2ApuRڂ,Uwɤj6mo}h_NUC@I5Mo/P=7ʪHP =+‘ؽYh F>zfntX#Pa#UGRh}RhѰoLP/m_zF@) ֢ 8L O 4h_ϴj_ ?j_ ?ؿ>bc8?zBme֢'M)~ѩ7&P.A.m5E-ѐ ǹuouc-4Fwu=Uѩ7&R6<%M픐X/8RNh3.:f@QE((((((((((((((((((((ήc-̶VL5Q55? A?mK;kI Te[}KR{1y:Y['BafCq,V1+ ,wo< Do^+?I$[s49cr[_^[ 0 eJOB)xoumR+mVݣ rPtHR] zn;T=H lRI5oZ:wG~LJ]NI׆@R"h*# Kgkafn]FH+Ük7=G֩7w3E2:<`u[Ehm (B2Hǵ$-mu+nXnԮ}F0G>,4Ao}vdh˼( 26ޭYMNFH2cTq}k08@K)iю  Yh:ՓˉlaV0@#Ni+-~xxx%iF{W_X^5-myEpҲK{ڍXmsƢy:_Z[@IeB{^`ڽN&}̊W׷Lf]^-n,{HO<unt[;UI\7h$qyZN=zEI__EQE]Wv  k#zZ,1Zar#;yǸ:VC'0au_|""mZY(T 8܃@Ǐuk }Vw34Iʮ0@zZ>Y^ju5YbvO*n@m5L~_cmG(ּuRSL7qmނW@#R:޵t oN^i\\ `qZ4Nh ~k  wI-ЋT{Y%w `|ޤW]Uu55}N8xӪR '|WxOCet'D.8<5tucm(?W5ӭm$Y ʪތ6gd@6q¦צ'"[Iت9#Tz̈́߁5gZT浊[C2Q.?_\>]lHFXI$+N &e rVMø>57N̳ÐAQ9=km"->,Hr5}Y+cG)j9)3'ZHoyJ7| owP[+P32mN'P?Z{Q@Gk2m!K`A=H*S |Ju?V3ߩI.N m(2+*;x6+:5nŦjZRNȭ!W%K>@G~x/֯Š(AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPuJCJkX%OsrW) gހ'ϪXw:D"i ҵ 2BZ֒@bC@VM׈]RO%C$_0@oZ[vkuQpX~hj(4Q\n[$F.GeAcަRQNLpszv+[[jF-#^VSQY9q\qpvbrey袊8B(o FO[n>iiÏ9? IϬ,'p9?EKuk-:ʗ71BI!ZwGAI?TW0쵡\&"&(uYO`?00 ڐjZh''ߵolF̗oa@ZGA9?VQt~Tnj_-;r>什V֯bt{Fxᴁu@7I= -8NOrɭ%)<3n}՛ ㌬sIJ`2F:$ 5 [9lqЍ[`H|#Jk:P_՘U`\q8?Ο]Yz[]\FoD\@fZ$GAI?TSxEC՚g6afCcZ6ei,҃N#+ZwHu-8N'ߵolH|\ɠ _=;rKN{`??*mKN#Sݩ ڶAQޟt~TNO-Ņ̪NU-ԚNo:0Ȳi, J;!b"h_SWQrjŠ( ((((((((((((((((((((8RJC ORwm1ig+6A Zh4j[>mɹq#Oؼ+tm![T*p]H׌תQ@Q#VNe7xV|G8Mڽ"&Hb\dlf7bNh %-%_V\xdufxSwTNǑj>,M+ڤ]]!zgt ,QX60 j|ȧA\3n]\dgIӌmw(8(֢=Z!i棩hi\arLr#,Gֺi#:9p # 2=EIr(kڍCl~}o n@1׫T~yoqy_Ÿi/ ZM՞31oLzPN?Z]K{%у|+Sbl+rB+w='ok}7QZMooֹ_^6xTKI#?hx9n@`PkwϽ7m鏼NM- G<3F-`x%&yf rOLVͩ-У>{_£T &́P`(O UǷ*o-hI-̑XA9M:xh^6$$u5(\۾;TÚ-p{_€-ο?Q꾯Zt7[LuWd'j7-G Rpii"nm$X9/%;UUO7sWq/I13Gok}7TWh˝H`9OJTb9~x (((((((((((((((((((((4Hb;*!w`% .%w"ȹFAX"y|R^{!ضϛ{_KRbZ[ W/.g+G l3OQݧ̩m !g)6F6'ۍ[ETȖ9/jk=/2_*?Q꾯 tw[^\*r3{|:v<Ē%DlF@`yj'O{[C23H|+'.>c5x;55s44{Tc\ZiQE((((((((((((((((((((4ť@>gޒSIE_P-1$/MXIƛN\LkwHҬ_;ԚEDiB.NJғbQEQE+PqߥYч(0;(уy4M2.`w_ 'LMBf;X'.`w_ 'GiQp|]6=  fhIEŜQ2Ѳ!UFN2Eu^'tB !LD+@9u/W㔆U)qyfv.i["*ժ(WE` LAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPwjJ(0((((((((((((((hZJ(QE((hJ PZZE(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEfotoxx-15.11.1/data/images/search-images.jpg0000664000175000017500000022104712616075370017256 0ustar micomicoJFIF&ExifMM*V^(if%0230+Ƞ01002015:07:02 14:26:59Fotoxx:trim_rotate| Fotoxx:resize|tonemap|NE http://ns.adobe.com/xap/1.0/ 8 855 1121 2 2 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((*" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?u]rGĺܢ\?\W'Ma ̭m?w_!Bo421\uqjz/hs{ɐ5B//]G*Fad c. *e` RҤ)G\GW5ۡ߳13Vw7 Wn~u]E4.cKam|/nqOcOڏ0y<0i"C@s~cjZgKv54[:ҸX<ÌlP%'yW֩kmCon#6N{S!4mN;lO{ ę۞f,zy4Z\K=4flη\qZ־.)x j[',{SX|OڼόZ隵̟k _ׯ|wiHbY&Rf+ y^Y#ry4En3$c<.Eak7q:+EǣyK5yλ4;Yi7EyrdBr8=OқvzF`m9&4(nVpaQ5qu'rZCHcXKF_>W|Hg%Ď+<ڋb0y]GRٹ}&w8i<+b$X| ۩cDf?GҼuŶ%֞cKqM@(H?֩>)fcZ7 |gk_Ş<Ñ=Z:hA<N[p^iRyY\Ŵ`"ȠӼsEcCmj{invԣH= AihmZIq=q[&}O[q[C ~wnzm6i1;{}}>m},ΎB, N+ۋ'|b܁"`[`Foa\Upחu3(q I/`Dӯ5[ۡ4ȳ8Z2t1v`F[qNwG m8X<i q:`',; n#lzn]_mѣof+|!c`;GurM=~w~Ӛ{iJY##kt`F[qJ/l|af";˒֖ۡ!y;fK = Hu+ɬ7(F2͒#B4}},%$-gH[|Lgzo#os}:ݔgaҲ ?Z>m},0xk}A/5[o EeW5}pF[qNtpCb+׹/~+0cׂf2Oji-E|LAW{ o7h4.c:Z1+ ,`N-ѣotRhj-ѣo߰[qG-ѥ/;;}>m>VE/;;}>m.^ww]4}}9X]< o7h4r)y4y5wo o7hatRi<֯}}>m.>w_?{o `B6A4(6m ((((((((((((((((((((((((ʺ-mgP!B.8ϵ-yii",#}3Ŷ~-앞[9R5^~555 EGY%+Ԩ~`ich N+[@4#vzM2lCq32dVLZo;;Sd'90ɞg%Mp2:ׄ^xP\[ŭ\,Fxz8݃Xψ/\l?v>T>% 1[N¹욮g,A8Yf3ղ}+]k^!Nim淑<ydZ|WVvе?"k9EOzaA\R>#&x_I(n7m8r=GZu xHtmHh^O_!s;NHKzWxBU4xVm{67~j0瞝kV0(.gqF8fڶg\fEPȀ*dN}0+EǚI<0xsTPz%m<r@ϡom4&T5 x&-#Y19B郭dx_֐劂#Av= nm$Oҧ HG<~5iAƥXOFdCՋ 8Ԍc&KL4/ldΡчpk5wIe]BT$Wm{/Z,e]IcF *sRٌNSy]t3%gˉ d mzNuoo-ŠMѐ[BunyXpqjxȒ$3C?{W~ [M^mݽIl#6>ld[黾it[:y A?B$29)?yigygiwpݒFː2qی׌|R|9-4,w,Q*ۓ 8[:Ρj7k}7nl*l&4XHi.zQi)4PQ@ 3IE-'qEiJSILAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPx$cIb$ 7R>cUlm5;oV]|'hm~O&v77%ʝv2@L 6vY[1nv8* {q1Y:ƻii\os?1>3sjsF-VO(=oTix],}hxGV'&:t'gӊv=Qcբ6G̈n\ lOZܛs1b$.y}i8BRy?!dRAuu@Gz7SM۹wwShtL[Tle&7ТlC1Ϡ`(,HA]4/6ЛrV p6ǰ9֍5Ƨb}F.ţW   րZ7Zb0'zH=+6\ֵ ./0\X4Yzvik:n%3mӞ9Ru{ogp$O*uFq9Ҏ/'56J2:9 e/pP4M Ju j~ܱAhbݴI&,{dݷzFq@ E.W~^=>ӴR@fPO@N3K?2zJm.Wv˻<Z4((((((((((((((((((((((((gŖ,} @<MyUÍ@Zjm.mWH{񎆽E/ SIm4LKxWo˂8` y陟zw~3[="8gԷIc~"`38^Ic_[x~am~ޠFFANOqM%xlzvqr!۰;1t^+yҀ<>t˭l>tt.!!\x+ 6|3lmm rnbYg=x^=3隯kcgjm-v-jH-p;j#VkEuhE``5amagui&<~&D>b}tE杛|\<+^VKKwj%9jvtn$戔9'g4x;[m<+{.HF$_晡KUZ:&Փ˸f́܏v``)b+F֑{l!qrhQ[iyy%A8?v=G#ӭEHsŮ洦MtWQxU׀m Ki|’~e+;N0uo.\/wG׫x^_W|ɦu,B)V?Ēyg ۝(iRuo-$sm!4#8X^5#*@eW0XL(SWj)mr¬Og뎴Y.5 Anл̬Um[$ͦŧmOkDH$j@- qp<oQYt{{i~֨lgP7> W'jVvC X/%>kv$3sֽ74Px_폌/VY9),M+<`mZQScHRJ((((((((((((((((((((((((Buf!TʹY>#XGka+ZIm)Xs]VaX\37P.P;QҠybLMN n`T{Tuf!Eȍkg,9%=-!fs0L0$w9?t6Lծb lt1Jӹ}vY7٥͋ʞsf4B[{d2G I=zVEu/ E[ 01$p$TWt.PծKWڨm9ǩ5?4(7>ռG09o |BCWҮmKiƫ).QssZ7>ۈl/^[V%OIc,8> 6 tWtTؖ,>Qѹ}:m,z\%R*:B z͞zғ.4+$i`Kl*|X(ֵ4OYO [ׂg1.=Uxn+5V5r$oVmTZ_ fӤPae±lwej:kKvR[;k{Tr* #ח\GF\f<ŀ t:ş.M2~tc2D0+ǚ|B4Q&&.85>\M%GדZ|@ӡ4FM^Dj~PH9#is}bH.&pіV*GѼ)cjvWy%ŭY 8Q{Rm#V:ޛmиi!spL5Ӟ W O:5mHbn:n qOn"RK3qf13Uak VQ @N:v!5V -]]-BJ{! KLϤEĀ¿$OH~n?^ v:\iRh ! *uK_XŧǍ|{i 7Viź̫B$?&A`O W|mw{#Š>PeWnzW_)pqS ]˭^j1ŬsurM|x"-Yqi4P5 b?Z^(++ˍ$Ys$9 GK{Ⱦ~Z # !yg}o5#^]},"3y0:@ڇĂϢ\sQL2U=l6ӵkk q<ۉ@#~lV֗0C}޽S"I_c? mÍ&UPΒ/\nThB;f$RRIޒ((((((;4(((((((((((((((((((((((( :R_M]IҴKoMs9`#3(L-Q$-[TtO:fQ6CQHζeSjVwqbe,^+&?fKcq  ${A@'o~ԕÖq۬WQl1"0;ӭI}uGfXq;`Pj+>㷻C$,C| 2$$кY#3$$Q~fwI^?4}Bp4v$k"w`N?혪|'kJ,xQco{I9<wH&?Q\kئOv (g3v/ ki6SIf1Ib?-JۺGt Ox ŢX ꉂ dpymScn%?ƗwHOϴ)X.dn%?Ə#ֿ٭(=A| ,GAm;SiiNv`ǰBd{oֳ{ħPkJmWLMYF㪽? ]Ͳ[[V[Ŵa+"]Z}ᳶE^X@ۚG O?-Jl {o}} ۺG OwH)5{o}}#sH)4ni%?Ƶm>ϴ1t iGAm?Skc(=A| V#wH)4 V_)5{omlF {(m緺B֗Σ8a~ckmiwV+\y.Ѯu vz\ 1$Nh0Y~P> gPhm#)BOzeҍE8ǧew[/l| J1Bp *Q\tm>xE{v1/nΥC'5*7#%oʏ5}gnw&S1pC nacK_FWV%Ρ *qwmhs\zRZ$ѬOvАT{RXWQ[3JϽciwqE}u&&^oʏ4u*ֵH.]UGzU;ZT뒣ZōcG[*ƋT}&-FfX-%/t-nK]'RDpOOz.:8u*_4u*u#U=JLӧbwHIG0kQ[Ȯ)`cw2><(hyWVyzz:5.7hXWQ[-[/W)uonP$BhXWQ[`Ozln$̤֋_5ߕru*jV bEV=* s>k0ZUK~oʏ4u*;MB)kicAv>wxfI*]eEWG?ߕP/{uyVa eA (XWQXVۉ)r,ճ&:poʏ4u*hhXG[oʳ<.4xrún]^Si:HCHzi3@W}Bawݮ96S#DVc` D#%3LjiF:QpHd$Wj|{jÐIĒ5ģdq8wWʌ+BVEޠf{d4m5C?m}Q+_ o7GutiimG|-_hjut}Q+_ o7@1T>ۨϕZSf24O֮g⥝PA%ĶMh]Q8trkb#bVs&4yEn~+ţbH=j'h y5MCGȗD Qۗ}m1^WR]GmwR}V0rivw`9ˍF}z ^5[Bm4G, qs&zF}UҴ]S^ Ϩ^[&`A$u<{b+8+;t|cP=r1(XmH* =!m5{-:ukdM[b>rn2s3Բmw=Ǖ6,:mͫ7Bz4dpHaI7ۚI,0<><ϳ%Ҍ$cu<œ]- $Yg,P?ph$F&{. %ğ~*Y" =yij-,zi^!Mo\S0̋bIa9&k췟k4}xƘWKcQFi.~pA߹F6T_J$>xC }mʧe_d_r.ӯ5/D[BL'-i+KOZKGt"-5a8B%?4y4jgIԓD"ijv5Kw`y`sc9#8tK۝v>YO,Me3] yu\- Əg@yio'4=;WqYVt` An3Y/ަn麍֝=V :de-_f>֚մ,2H>\G.c-$%999[z^Yj%9X'$.  #Gj#VPG@9}4y~%|HcAp&Z5 p5}~mݜi"5ax+}gOS엟k4d#_3=bY櫓{1&H¦TǻMhZZחUѵKJC2]GDc¨ zzOH/cx4c>h&+)(6xW[F"}n}Ra0=׮=+-9_P8{2R^h &E 68pkEXijդz\z{`'_^uy|<ƚU׉F#{Xᵒf#wg]oy<@Xyڢ-h-h ڏ0{T_e_d_bS.j}&1 [e??/[KFt˜fm2LOFt?SdQf2d~m[;iQyg*u5\e|(g{+ў#JY0HmiHuoJ7/c}hq7Ƭ1שn_ˠu " b%/%XdQ>m=5w.*0CjaLGr+8*ĚY"$CR2yJ 噂F3\Sm. dUܒFr=Ey2j'ԬԵX\MuHFpL`1c(49gtdw68:㷭v0}hxOjVYغ)Fs޻ڝe6-"KkT$quN3ګ\Z\\E$1s!'s^E;5]b+RxRd[qqRhDDl!8`"ᦧ> R1;an. 9WQ`5BYd;B M*Bpf卶Io,m#co\V&h35߆` :u{o?O9[TQ`_ޑ>/zGsmkO{#%+S;<3&zyO-m`'ʋ$|xwLmy[xbW eA\V>SNooeK(D:y';L^z>&@v{ 7ON眚~ m:"~Ă.W?_jA?^6-R){Qp[~BI13ޤww6j u R1;^h #tlfpYcRTH?QU^ Cm$~+&Woxm>*ƾfщ }OJ⏈|7n4w7QZʻ283CV[_eq''?Hy?q-|1u® X/IN?2iK1Euё }i #}_# ,ao ו|YnTn b|A}/Zңf# 8p &)LR\~˯ޑ>˯ޑ~)IumٴA=mwhbë`?]7NҾuӛj6vQa?O9G|A?zG)<?A5#f1!y1!X gmQa's #}4rù_ޑ>?O9[TQ`_ޑ>?O9[TQ`_ޑ>Ȼ?O9[X s[jֺ6\o5`7*Vsҷ-ԏrMfk#7E#DKZv>H (8 Ԙ1ѓQ0hZ3FI@j g hw*3;ռԸ";k8R#R8aRIbzSRB$"mbAoҬj&?۹ē\41;-9ާKn ?cKj?cKj\\iv<Xٴ{i{?]0XڏUWh6/'v׉ۘ0-BprwzP"Ao: %3%1)靭Z?ګ} 'R ;?}E~{?p9j>j>}~j>jk;$:_>{?߃ke^\K8’ 8 .s Я幖Hc '5.=J[[g{M+HWn}~s~s~7}},z)k$a?t}~sȑsL,O= 'o??]a?tw?\ҙ$L 7.yqGáa?t}~sHoSϽ r M 7.]RM5tebv*pq 7.$EBPM!Vw.ǹ'Z4}~sF9]dc .ddܫa c'RmV9Kc8qo}:Ѱ-HH71OLdcު[ޣ5v#O%cDp A޴fw4ٙx|[@ӈ.ђX̛0Ns*~ӭ595[Yu/ Dqm]A5(1 )C3@ww/q)®y+`)ܽ] ?E=Ŵ2d1V 95umaVEqs gH>LG4XcP$%#-HtV~ jτt;lo7}Nr@bMM]AoZ Eۋ\Fg^/%Ā*yuZʸaU110'McIc|{.mIkk4 [|ՌI5>/k X/8&rr/=wGC vIH̛Pю*kM6{KX$hU(@g^#|e$sL]O5N}nCM%mu @G)rGtϢiO%Ēiv %;5 8n>a8f +;{+DeH1Ar9@uo7?:8& W$lp9gG/WK]k"ຂvѹ79^.Mxrt崭 +[#φcuiea,M4V9֍`%Q[\iVB7Fco%#.{L-;PO\g~1/#T K #iuirmV'id שAK4~FK(@qGZUU5 ¨jENMXbaV m3YZ5:y ݵԒ՝c'ӁDZ]x "[)#KзHwg\|+MV|@u&W,[ix=+PҮYYHRs=Ҙ}$6Oe-JF*0.FZ9vѴ\R4m4ԻMh.}h֥FZ(3,$5k_i%},{€$kfs^6v\!l:WNx;{Oq;"0nRۂjJ3`鄌cܣU4c<7% {ncG.zYP1$7[i5on  =MYYˤ8ª_n`{:>I`VV_l&є*;IwܕBFP3^8Su^iϔep]l> XT{]*Pss@ZxEkpY>`)sx>BQU[OWh `OUǦitS@n5 }>8$P6z.6S>sRe>sRe>sRe>sRe> j]x dbDaN?k}Mf+G; +_#[-JlαA,vǠ5|Dn4kQl` D4c{SQ\,l9A5KI}뚯M*[}6K{MN(+ōFdhr|UifP%;b;kUj6bhBE??6Þ~ omiV?033ICu9;]Ex?r {gٱ@J3īF𾣫hWwVwDa؏w8GmGMKKFgʜaӰ$cӚmнM[3p RNq4_Vч!5ω/O"MZEhEkg_/_ѿƏ/_ѿƶh _/_ѿƏ/_ѿƶh _/_ѿƏ/_ѿƶh _/_ѿƏ/_ѿƶh _/_ѿƏ/_ѿƶh _/_ѿƏ/_ѿƶh _/_ѿƏ/_ѿƶh _/_ѿƏ/_ѿƶh _/_ѿƏ/_ѿƶh _/_ѿƏ/_ѿƶh _/_ѿƏ/_ѿƶh _/_ѿƏ/_ѿƶh S_xC rkkA(#Mk%ia'CpVY//-%)?W|A*.~5sC`9EtK+K_NzR$Ex])?CD$1j2zm#Q-$7QKC>`;dy (9[9bkT泭SEKTP= 6rm5kδWx6j&K#f06grbo>/S?cB7+3lV4NWMf5YQK9 OAUoպuKۧvKa(g++o%w6cogH!9@G+N.gp ͓ː>hyWݬS,rx:4Ok>*40ڭڢ:N6h I,xII9&IBʒpnjʠd*cUx|HAp1>tX?1yֻ2Ӽ\WRyH`EmwlҢZD,N.-39 䪅~mښE:?O7@#ZA:ǁR[+7Ǜ*6NOnjiڸw~Sm)m>Ҁ*z!/MʀK麬Ҹ>}?fivRܰO2Vn=֩7|>uř@3Ƈٟ4}/T&V:ai@`Ǡ{M/5[8.I( <(~}8MV\cN}Jo@LTz洚Ug4ϑ۴%xٟi>ȅզ{+>@>iƜnn |Xd30g8=8#(58Ky0F6^*YKf\b9n' 2֬",v %} U"kvDW?St63* H5sIBXoAº]k'KCdu' 99} _VsL+ +} >Oڏ9 ($@W,GҴԏ^z@k@n&C,w#pkI/^D!`V ڠyE+֟%٥.yq/̠ FqSx:v>%kͷp8>^1]u"l6zom|<uOL8h~ tK{m*ͬW+F9%:+0 [x-ow0!^YEyBq&r@lQ]Ayj$RcddQڬe Hex-qs"lFW9/ Kx-0WpXwuz=*g%i mJ;m*vWw}ֽ*ു6C E+7~5B5m&iZU޵9گx,GMaxGmdi!?B0N}twżɝ)RGQ=P1k"k2XJ-u>+]>*OHi˓5R_]Ioun.m F]5 v6g vg37㴿f?O73:yu=@鈏f#G m56?RUso_ule5jFyv.>27p&U^t>FO"ޙq \[ T{zuͪohZ$𖳥!u .ln&BAQ^ Lu=[Q̽(E*P17#5 GK;GTmQh]?^GVj+;uXB2Y߫rIh\x@ՠY U$pQ\k> M2 :kd(nqb~lߌע@mPVeϻF,JAwg ԕr6.ω+ ${k-leJo͠ ?{K7yoމm'Ki#x`*7C]WoTd6.'Rq+4P <մ JqqarڂOPcu=. vm0t72'~Jֽ_?(9Eu :tJ! |c<5邔Qp =?? 8 3vftMkOԦa7W$HfWVU`T|ף ݴ~]k$Odxݩ7 ?kQuoU糓P%Gt QnORzwmf?ӸhmgnѻS/;JhmgnѻS/;EFݩv?_vS/;FOl Gmk;v?_vڟ.6vOl ?7:AQ(ݩM սWX1|9cb-HrƣM0(ֿG*k}MS{U(5=>q!O,n})=NެDf(c$lKc9c=ǭxzYZ<1#i#ݖ@40v1<['S-Yv4λ7c8qql({E@J9@p:s3^_o_Ss\^G,K$siwBMv{k;ɦvɴ CLIլm"ig&I{uU3xJt!}S"\vWKז=+LIPiGɅ@g4LJ.MKkDe''{ 7G-\](5ߕ0DqKҭ~*Z+$ydz{ |I_ig4WS[F<ֱi4΍gh2DAe>zR[Z~i] rmϦ>=“[8^7 X~JYl#CpJUHA}8+z',5 7vwtUA~AsBgķm(@d?ʵk:cwG=8\1Z(>??g=OOFjg:?{QڀΓ6{Qڀ#=i{Qڀ#OGRlgGΏ6{Qڀ#=h=jMlZ?Zg=?֏u&j6{P?Οڍt~'g3?=@ҌzSRjm.+p;"ci-485 dփ\(l.BE@kEWփ[ObԖw3[<ڒFFH2 n#i23$m6\ddG \moP:>;۵4KlABijc؛"92R{39yv7+1SVyp*dc=h?5=OhSFж7Rd^ %HȾ?0#<_E'B*_>{??€#<_E'B*O}H?˜[{LeG?H|H&B(<_ELUI,I UUYBpzs@o /""o /"<wrqPgw N![B{=HcEGJ4=/@}o /"N!D!/挟Ҝt}S?A;Rygw ?*4Cz)|"0{v <_Egw ]p)]6e]'d?gw N!=&hA;Qy'}4f!<_Egw FhA;Ry/znA5>h;xB$I$%6vzCRQPk3(aEPEPEPEPEPEPEPEPEPEPEPEP]XӦ>~gj2luB/|GxfuuBHUk&_@.thNW-fTQEf4 [.G>DreH ȬgG.;uk.zH9YMi+d۵=sL{al2,Z :`.qm5;mtPi 2n d7G$w} u߶I66`o@N1j8јx!`zAOP[Z35,hHS}E ,6ԦRhn :ɌWxCZ5*\_ztG6%=Nq^z٤tfS1~{>*(OG7rJ|Ōsfue%c$^3G6`1^@5OGrG êa@+j:6jvrYcˎ O|g"k|}meXwJb@7dD'L*t?inUuƎ*'b܁8u /kg649] ,oHCݨu5O EO2+,009}?*kr/B"$e*m0`f (S^[q3?JPSd KbȬ6`WK[jvZ_³L0ѿB)mV4:H+~#Kf=λfhȾG#󩓨l tZsWԆ$W_ռ;sn5[K^CgDzuكOn 6[V`T8k >n~3%ԥ<5kstuӭɀ2Sf&~#kI-_XOFTȯ{O\WGxr=$Ӕv3׷~Kí q4sEv!Jzc9xzml՝z|"Ywb+8y+\: lZl7I&@(3ju$B,jwrI<حYvzmԭ9_8HX,@"A"ztb,QdQP}LH*psz{NcIM a((((((( >x&Z@4U3G?e*?z*?z*?z*?z*?z*?z*?z*?z*?z*?z*?z*?zl$I#޴>gf0~ͫqkV,GluҿO9[gt}?CuҿO9I8/㕽xh<~4w˽I9,ǎh\" 6v}?C>[{b8kfݏ}K_4r~gf0k~i?-k.״r~gf0Z'u{[98=@3#ng zpaj?C>ܴW1Ρ]%z: E?4r<~4?4r'Y+㕻xh<~4?4r'Yr~gf2m-Ie]֭0Ԉ5Yq#"{&h=<oЩ(oQB#ɿFM} {&y7*J(=<y7?ƟE @7?Ɨϓy?ƒJciYcHԀ]4gΗy(m} %׎1˞x] 7@B:_Ъ>f@/y :_Цs6©y k-\ɿ14y7*?Jty 7@?oRs,T{_׿c$NDL?4tY?ƨy 7G?KtΗy(m} k-f@/`/&7zk-f@/<oЪf@3Lg}ikPoG\ダzzB<њfM} 7oSFhy7(ɿOoУ{&?4fB<њfM} 7oSFhy7)T~d+ ӳ@>HO8m9rI =qCv~Ǻmm&e *kknc +O.xrJ4םCTgaQrE :۵;YpqNҢ^uVz*niFG :+?xCU ʍ~T\,_nԏ,jȬlsǭ U~%VPyf 60e9= U-#et^wRK$еɑ: 2F3$6sogS4M㯥]EMYjq!vҮ#O[tp̓m]8rjTf.o> .$;#a:zPH2HJ9~/ƲiZm~ 6N0(rrx5xgDllS Lw9$5?U/g)?¯'N{C6vqEMtis # 3e.nӤ9­R?ZԇM-弗ei;"g=Nt>Jᶅg{k5LT;_jb|D#ZZ$y/ej>=<_-m[FcԹ;;WxCLgM5v\A1IPcxWgS3{#j6(,cB1:zh #Tm_x~ ?1hzW|H,4+]WkwmyjQfH5YWm?s|nݹLc=Zߤig,-xigUmo[ZK5nUpyEuzd|{Ey>j:GCYLmٚ\Fe1z]4w' ֟A_R|PY~г\c sRT--ɨ*{WU#kfh^6p ؟γz>FKYKź@qBoڬcګ,q [hU<=[h:-bd-yq-8TT  EEKѰTT  EEKѰTT  EEKѶ"##x5/H>!#@J^ĢJ)hJ)hJQRQPWϊ\-75%ΘJ$}WQX)iMz}+G\𥎧v1Ik+} iVz$$[I+[5BXԻb:sdij-mvu V==G<~w, U 8^HHFa_Jwυ|E&ӫ_VPJSDOf쳈tKftA玵iv6kiȖlX,>oԓe[KpEsZTk^L$2rWp2?ZW&kvz\iIԬ fj2|Nfy4yw["ᘑZ迲t:SeIqnf`&6T:3O.a 2%9.Z-|Q#8 @2) G/kgKS;ߟtsTj =Ho.w30栿ރLGqf zrM;OxU>ĶPi|x0<G|sڄ\vQ[iPᙥ R1:wW/j~0H`kx-(1s砭=SwΡMddXjW~stA|4;d]F!@01Ai^gL=8Jc-4_xi.lneya.OOCO[356sf̭9_/9=8}`'7-wC6p~V@.meӠ&kW,xz9?CE/~%jSM)0qߌihm5[,km_ ?( n\xsAm^/lvwsF8\m<3֫j֑ivmjP]8 xR_EϥMHd1'=i5 H_ip=`fXá8<\jM_ <" ?f5oYV_ji 4o$R㓹qҶR֮u{]acZ<:Bb,A 8V#ׇ{JVg Lwu Vf![&>Po]),J(AEPx=0I'e{s^ey5,orzOh\Mi{IDv'=*Pa"W\D/RŭlV2?| N԰n UΧF#@1K㎔kz}+ȵk:$j#6dP a z}n}åZ/g)I*F j+ g@D%7㸧a\( 0\S$Td FU FOf慣}[EPM)|&p Nx5[|MuK}ZnJ.¡;H C;&(?!O.y75IemU$w?9 ⏊#MHQkũIGLG?w/eO3n4{,.!+j[A60K^2yǭf[xX5ͧ[=ms!y?+q`c=z7eS@ծt9 ;O217%@OJ|/xAọRc ȏJw' 杂3ŏ]&+˵ψ]oqZƖ:ڠnzVsAac#ܯU]%Lp!dy0(\ V̥]A#wHXd c榻mm+[٩] jv ="yP(\o&$[Vfۜ7 qqVOxD$ R`t\?/QE<Epu'M=63R}Ke@?*m*}iﬥLoXX`cc.w^TYU)<ȯ=pMC-VKsmiU]3Z -nt'"i@IcEP(GOCVɬfa385z@7ɇx| < ʇx| < ʇx| < ʇx| O*)QaX"OXB:֑O_5BbQE(t-J-8Y_8 :֒Ln๼iWX݁ʇ]j+Gc)6?̛ xr٭4{XD~+&ѭ NdHʜ!#;J% 6?€3_:6nRcYG{ uχ4Km34RI3F0+Ccg(% /g_Ce q;o$I 37q,4s)U}kcg(%  hSit"ʃ"_ihYh֚}M2/mFA#z]/QKh>,|>4Ru!b] :j---" a"AҝKlc7S-+{K}CJ)Sda?:bZt^LrEmI>k{?@=#5tib|At94=BӊMnp4K~Nv“cg(wүewJ@f~1Ku6\L\MC\?&6?'\q[{_{?KKatvN'Y \Ie얿nC%|)L2gsK\,'o[tyRg(/Qp}}-%mK_ o7G'*Ok-mZK}>nϥʓ{?G'‹k-mZK>nɓzII=__`mZK>}>n*OT% .ϥk-/QI=_`mZK>>n*OT% .ϥk-/QI=_`mZK>>n*O/% .{}m-qrn ~Tju'sqEex(Ueᘷ=\Fֿƴ g4eQ@Š((c1t1Ӄ0yjJ'jVM&[DKjH|p{g!÷ I7k)&^;_xu,mK[.`edU m*O+> u]$jgѣb]2PjxKQ֫ymklrK_ )#4O>7wv*Owp\pg|?^CיO. gj{fվf7R0w?CׯOj|K6qX,w 3͞Y$$ )n3dn'vagfKr$l:E]Ӵ+ "m6#q%żSU5. Z CΔel|r1מ\%ψAu2ڜJpH]A8 ȥ?]'ZՍnֲ L'؍Nq0ǧ>\xk{W_gk+ ֵѬӣZq ?/|㯵yv[Χ=v)i6㌏ګjt0Yjzijw4ef(*8#G4eN1M*JR&!du _J~XA!M7X*ny223N*BH /w{$*G Iq]C{K~͙.qdIF{V]3RLnR]=B[SeP1GPp+i(-庸j]9 f-UL1k޶wgo%2GX :Pq%k3J}2ͮ4u ZIWd$^F &,7\56!%Pv5gį:8RZƤ!=0zP3׹.-'F5ӛLFY 8h$17מӮT/gI"0<آ}Ia/+wHR&$BN?yKMҎ._y/\xܥ5:,q<;vlq5(l-P4--?Ю+$o!VkPo[RqϷ>~nKmKT2Eo!C*=9Uernv?Z?-?У Gmhe-r@O(i~Z((w*ah B-U22_-Q6 QQ\6 ?ߡV<4 ߡG@O*יF4 ߡG@O*יGFr@O(i~Z((.Ui~Z?m?Ы^eeʿZ?m?У Gmḳ}q;ƙiCNч-?Х\X7,w] 8Ȯ|E< M>M|4ns7Ovv:#,|+pUaPkͻOPGX4uC@|_ZZbEPEPEPr\h |繨i{VV PEH'jV"I&!cK= ̞0USʳr-2cxڒv?rM/!w"SC =WYFX6c2rA=Lus=->w0#z0=ӧ1_[5)quܧ!VMs$f|(|0s)? M/Lƕy]Ze i C Ez%qkˬwxK8MZJ `u(__6~,6ѼDC  tj,kzei &ES*rjHOsSǚxsCapsd7nc l`&vkG$xO{ICuwl7B9~=#ѷSy9sTЮ'#es"9le$ ?xXo\lGG 9:%][I04?:tr ׃zPᶷa m%Z3z㌏Ǫ޵yOR X.߿p$ i-zxN}i|B鿧>U^Ͻ?MRG{WlQ$cL}I{hmK6FmЬgQ*۲6_v˽oo@[gnp$qUQ׀8RMHvIaCZ_Ͻ?M]-OֳŧFCgٻ9(Hȭ X[IZ\(%]r2rx*}*9?2t<'}h +v(慆 H4mRt<[#MޠcV{>ywo@A U6/}0y,89è2wٱ^Ͻ?MDN/ndgE ыYAHې(|ox-Z[< 6"Z |eP nŢiP-.3p&+nS75i^wTiAHO)x-r?Zgg<eӠ$x"q R PЃ`S|ʽyRű3̓ܩ?0 : X>"Q`֙i&tg~呀`s}'nNu'4I%I_B 8郎1J<=]ohng?I)mqG#<aZ=Bч5)mqG#<`{CF_#<^]ja CY>\Qyv G4a e>\Q)mqE-ч5)mqG#<`{CF_#<? (0?\QS˰Z=L?k/SG^]ja CY? (|)mqE-ч5SGyOk/.h4X[?|IGyOkC9cu[/.h,k >np]w8XOWPх4Z|aǛ%, DVjZ5/x$eP1( (^sRk*kn?ק֒Y|*3@Iv3:97Ğ!N-!knv# ȡCJ88A(mgTtsu ދmsT ,|{vm)ծ4uz[+h<Dl#B|;FGo jzf=m;2;([֗V׈Z\C:cҧsҼIgڍ֐ǮE$ƒ~xkf/xxK71Ũ$1[I*9 IͻSo. K{<6 g7&}+Ĥ5˟Mk/,XtTzƴ&n WV}<P/:K#%9b)(R@ EPO_5B J(( L뜟kmoZٶ<w`|"`\{_R5-®(| OҗQ'_<23]Gl!;v6wxj9uOmnm-m+;}͹$`v=+Sm6]Au?F6Cu)gwaYV1$`>ec~ք%fTÌd)N0?; dq5Ŕ` bDحMv:/7P7b!3Z6ĺnkqY$OsB-s=ƍa4y%nl)ӵr5Qnm9l2-|ʑh~+-k*1j#d`p~us%#^jM *v2JpGZ!-n`I?(wkZU3K=IB|\{RxU nK2yKmy݄"}Nm 0 Ђs]Mqk*(Џj} <(NX;x7[P5iMl8i-48kiqլW>e@# ptKVo.o,YiCy2+tI[K/)cam2^Ht"}sLTM2KcAmT8P<©8@͜\şmg+on G9]$2Ѭ0du :z}!5mj5ƥ{[u`Hiyuݮx x58,w:@ tVEcrF$WwO5>+fXk7 'x +.Mk%o,lژ kBx t`z+X$8cRxP:ohKj-ygmfMq pA%ճ\u;qԃĺ;Iww<֨,j2q@ ;6Xە$wsA)4K44Q ڲNEvtB6~k?VƏZտ_NbRќp֣tO4^(2:!A=IXr FnZ?Zտ_V[qm "Mcb?EOש1K-ylp4 Ps3HVjvv(ndN3l](./V{UxAV23IK+2k|;H`d+[v!g.@/I.'VI? ^ac~(c~(xM}O?ئbi? bi? G}~!iMc~(c~(nm<%,O?t߅ _=%h2(gh@fX.$w(֢'NrUI8ʄwP~%f_2xA.)< xF4& wI?;HE}>οLC < gtHt- ulu ,}/ ?GMR G? u ,&k[?>C?Z?l?=6@?`Hgj-6G@_ rn`?s;ß_0T?:?l?6@}0y>`GMQ&(A?0}|P` u ?t:KƏ>~`a~):IQTz?l??=6@y_eYXz A߅ 5ia)3 hקjOBQE0 (3asxφ| M3[Y&_a3AOGcnRJ6XU-3^)QXK-绗0+*滝р(mY_͠0$t9ah<9i" }B/1Keuvw/p8$wШ=i6΀8{_ Mq$CulqK{0ɻ1`GjKռ;jҤ)1XWV%R)p: fc@Zf*ȫ69ixb%z۬KOB4F\SZ\S@X?)k ZŌw "7ou̳|nmdџi8$8s޳.%ygŬ;gfO$¤e%hT8UcE,3Fb Մ׬3imI7G ?UL?ZgajwFX{q3_ڟ]A:p Kgx㓴u?ַ^]Y25>MFL8]\6m7 {xs)zee׆h]0GVV.d+MKDմı{9@" >½5i61[xb)9GG>լo?ƷEZJoKivoc646Ѷ}@4}hXF}7Ohmmgh}p/(泾{@4}4ihr'>gPѿD%- HUwiL/"AS͂Q*#?F4R<7𥸂HnrL3dA_E{}jL W 0#H~'[`)MvsVO1]⩭ͭ\BϨEvHA"%B~]sI&: Kݑ*Ηs~|c jo irywq%{7Dל+ O'&ᶍťk)sk8x<ne'wFj-6mv[Pc7؄Sp%|s7q֚խtu̒y$gjzInkPpfNs!~uv?7+F̓X 9ݞ+K2xo0s͆]&y$m&87.do:՗b򄒻dqqO.Yjfjk4G؂MqN25k֡ A$u07v8>|Ki-_}x@=Z|rE fBL 3PyS|Wsm䏡o]l%0W#yE'3yE?U(r%@Qv@kmvK;9gygpk em^:hS!{EQ1L (pω0>cSޭ(n{\Cl'8c垼jH}>Tm2,eK*K6k)- qю8րGUK+u|L5ž$,XyOZkcoBލӲ$"lhriGy羉a7 w-ǍsctHVk\M= Lbu<SI@QEcF=SЍ&Ѭ?ց??ƀE˅KM>n.<5ɱw`gyiC>zP*kwsV1wx}4"O"6ъ`ci?'I#]t Q.WIQLElm?I?7}4 0hm M'd(&O"ph m ]'"O"0h1x|wI? |}4߯e~J9)-#hqGFo 1DM,(R>8:};}-_=,trr =+d+hIb1x| w?J? m =+d(ҿ&O"qF(w?J?7x| Q&O" ]+d+g`6Ϯ2n_'[84`6Ϯ2n_'[84`6Ϯ2n_'[84`6Ϟ2n_'[8LEA>zWV(co?_2ݭVA$ѱ@9mm`cTn#T!^_%QL( ؏L뜟k񶍤xC K&}ܖeyOӊ"srڇLvv:l$Fc!K'<sY@nmWv,;F+Bƚ., h622b6B7s¾juD)ZKv`ns鷾uf-|㦦(]X☎kuX;#&]7cO 뛶N ĊIuGu>]kZ%s:Pvuo,ʡ2GsrOjqlEs{ֵޓ^hl:L<138ɦK%=fq >S2rH!sV,Khtȯnrш a6ݻaK Vx/Mښ6nw Wom$K]AɅ3rڐ9aCsIx[PpK #5qfxD.%X 9ںML $R;"[p$9L ve d3$T88;\V5h5CI[`A Fs[mExYXȱ^x(-Onz 2"`PO$sYz,.#cbbIG? ꚕܶm} P]#YŁFAl3ue-RXZ\_P7^MN@ @K/[˩ķmo!#O-y`9R%!\'7yi'pER˃ b4 5"QJuIoВFJnPU#Q~E.o˭NiHX!*yƀ#O EO691+ vxTaoA}f&.kGhFWR7r~sA\.>i8f bD.{wN±5/W;qzVq:6,>Lt(PPȹ#ɩIe+w <qŸ ۊjGj(cF=SЍ&Ѭ?ց?4W*H̶gB F f?:M7z/7@ʹmOict}N6'4?1OictM;z .ct];z .[t];z .[tmb}Nw]nmmb}Nw]ml&Q.,Jf,i,.˷CGR::Ҥc)~V*Ic1ʊy*#fF6zp8,} ~>ɧQoM;z .ictM;z .w]nmmaN&=G?@vt?1>˧Qo6t?->˧Qo6t?->˧Qo6t?->˧Qo]M;z .w]mmb}Nw]mc YI[.64y[s[ YNGO'XT#^Zi!(b (3aryU ׶MmwmϒFFq^뜟j(|1BǦ[|ϙ?w ?`ki=nb$/byۃE?Ş>Լ<-it{Yb9+a®V=x/xz-2m>="l17<&ѭb@W84q!׭[igM;\8;J99q*o xZԧ༳[m6+Vb.:~sJދ+ew=Wic= s}mI׿om(mb1I'$ ;ʊK"ppGr+pX#k-ʭsne'a<vvvZol8!Ah:*YMLj/ס8wc꺥?c]1ob)BXV::sxR|;kFQe0ϑ3<秽v{I۽&+fp_y|-ƑiFT+5SS.)n_>ekIa(cIs.v)=j^/:d-q%`f-ϕ9'E/>9De`_ m L[8{d^8?O6˔ w!J9$sù&]?dyGH>]y?n(; @y*&u5Z]8'VQ\sRk.젼 0 ?km>Ѧ/t&$gPiN牣 h|CiZI,,4N`X7)z,CHe"c)LnϘ [x[C+h--؃.vndiOa(&i{$ۥ+dYpkF Au m[yega8QB;;8-ϗ >`S6?lSsMAH>cT4>xۻ(vl uzaJV_*i95t8^GLF\0a yIXipgq4OqvrwߞC  e!Ksk+]}/_Vk Эn$$4{%H pʮP#Tx]ԴJ+m羓Fb[F߷ohzL:=[@HWCgv,I*LI.-I!691ˏM6-dӺ0cz> Hψ- C=ߖ n9#I50e/A`ty&{Iר,IQֈXgM}:nV(`sն9 IEsHTZ(OHS4Q@ X?7kn,?o`mϗsk@|@rAkzh-u] #[чM&/R/C6Іd` ` e` ` t Fѿa` t Fa` t Fa` t Fa:7l??µvѶ2toX߄ ?toX߄ Fѿa=vkSNA @`h*cHf!ptG-\])#*ziP >gPѿD p4#OFMVZMe` ` "'Q&+Wmh+FMQ"'V6W@Oa&(a&(a&(a"'Q"'V6W@G@Zh@_:?m& 6mzkXD-2E$qXtVh f=Ư}ƯH)QEm뜟kSe![o'ZHhQ@QEQEQ(R撊 Q@ J(}h {яz(`f((((8((((((k=SЍm%zL YY ̷tIC#xm'qSƩ]%XZy6".p 8'촡o/P2%Th\2_Twx| w?J?b/h\2_G%UGw?J?7| ? GQs@UQҿ&O" =+d(%Th\2_TwkLEҿ&O"/h\2_G%UGv>WQ_M+d(%Th\2_TwkLEA>zQ?s@U? GU -+d(ҿ&O"/}붙/g2ŦL~LEfa8w- ,Yk+K#WI^E$U <Fyƛ^-#xqWF)5g l4J(R>~v:k&?_ GU c(k$㔻A>zWS{B?./;A>ZWQLE^йd?* K⪎ϖ2o_'@./BLEA>ZWIP Kйd?*_'F}4 ? GQs@UQݯϦ2nJ?s@U? GUi_'F}4 ? GQs@UQݯϦ2nJ?s@U? GU -+d(ҿ&O"/h\2_G%UGw?J?7x| ]UA xÕ@dPo?_2ݭV$ѱ@9jҋz}j·ޯHlJ(( ȿ+m\kZm딟kT(`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE-bXǪıTy#Ik>OYU5dUu- ﵤ+_mC1Smm0!Ѷ!Ѷ!vRqF*mmqF*]lF*]lNAȧ@`i A{ >fEO@K dԎgN!ncbGhAM?ҏ LC~ɧ_.i'`h `h ߲i'7,?7,??€M?zt}O8];FEQ"'P~ɧ_.i'`ht _6@ &=q>ɧ_.&(aϲi'w:?l?6@ &=q>ɧ_.&(aϲi'7,?7,?7&=qw@G@@ d4hx,GO'Gvl_sifB;$RGpLh^?Zj?{_4ؔQE1Q@VIֵMeE![o'Z4J(Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ X?7kn,?o`mϓsk@|@ToZk(ߛtIC"xm'AG./hwmj&Th2oLEݴm_7? EQs@ɿ_U]Fڥs@ɿ_U7? EPݴm_7? EQs@ɿ_U]Fڥs@ɿ_U7? EPݴm_7? EQs@ɿ_U]Fڥs@ɿ_U7? EPݴ0=ɿ_Uo< 9ɏ@RҁPK:_m%,hao5G$KȤ@`J=fZMK`s>2cs@ɿU1vѶh2oG&Twmj&Th2o@vѶh2oG&Twmj&Th2o@vѶh2oG&Twmj&Th2o@vѶh2oG&Twmj&Th2o@vѶh2oG&T{meC&Z'%KuL?*\]USfW'SiWi1T?]+4iWibiWhu|Q?_Lϥ@F*KƏ1>__c}.?]+4biWhu|Q?_Lϥ@F*KƏ1>__c}.?]+4biWhu|Q?_Lϥ@F*KƏ1>__c}.?]+4biWhu|Q?_Lϥ@F*KƏ1>__c}.?]+4biWhu|Q?_Lϥ@F*KƏ1>__c}.?]+4~,?opmnGWjD=?&Z5/h4Q0h44`1sFi0h>gޓ\џzL0hsF}0hϽ4c4` 3KL0hsF}0hbތ`у@ z3IF .}Ͻ& 4>4`ތ`у@ z3IF .}Ͻ& 4>4`:hB(b (3>Q|Ө1?04^=(??qHahA|Jfz??J8ahA|Jfz??J8ahA|Jfz??J8ahA|Jfz??J8ahA|Jfz捲|4۩(DfTPzdSͅ:_Pya\M|4m6O~S)?$?‹ldh:}q&Gq͟:Hh•pQ`&h6Vܴ` *(cf` #& .?Z,h)ӮO~S)t$?‹k|4m ?O>O~S(\6|CO~)Z[*]׈(829`('BHMmysox!.aNM;m?6KwA)?'.'qOpdh}߄ ?jN~Q`dhPGVR})+yD?m)98?ʀ5gO+Z&Z=(A|өxahA|Jfz??J8ahA|Jfz?:nz?:nz?:nz?:nz?:nz?:nz?:n=4mxH aHYO@N?6/}? I~S(6F>O~S)E&l6n-*\H<ǂ ;y'Zl&h:}2u''PtO0Ok|4di?$??€ |4aj;3=FnV@cOE=(dh4anMr%y>Rߔ M|ѵ??!')gIdi04 >z}\z?!HD2$DXzօЂ9x#!Ē;|"֭ڥqu]RWնs.%N01s]O،I'*̚ݥl'#'~U#g2aymo.IrC.C$jT SNWQd72cyyIEi.y2(`zo_3zn݌g3LV9!xk]3A\ZnUno:DB]Co9;aq׷Z"#2GoܩYt'$}@-Oi.P;_u5m˙m̗&H؋pn=W~[\urg'yoY]#/1pF ZDt-v4vcX_V:lEčong||~xzI$iilG!U4ѣ1rp:Lv0.o=J" d0CqeZFկb 7:kx2P楨{RxMR|*FNް{}Hml.#bAQSXæE%g+F?x d ]Qq-ǁ5fY$a{xps[xQ%%U t=z}J-t%DVsSzЂƝUk6vy 4 (Q@|?i/Qk@ZPOmk Rl"Cn F(yrq#a ^>5E 2Nlzoߥ ֢2-o~(̶}_j(+6qiG/EQK4:9s iQ@?ٶKf[>/VͶ}_2µ 3S걨1B*ÚE0JH21 H'5vCIƣʓZRIƣʓZP'*O5hQ@Tj/R:} p}_Z)Cʐ R< Hw Edf}+f>RkQ@?ٖKfZ>/V#(Iɜl)%Rlѩ?)ٶKEdf[>/Qk@ZS@df>/TYG0TE0)EU%HZii  ( (?fotoxx-15.11.1/data/images/search-images-metadata.jpg0000644000175000017500000014310112616075370021024 0ustar micomicoJFIFtExifMM*JR(1Zgnome-screenshot6Photoshop 3.08BIMresize tonemap 4http://ns.adobe.com/xap/1.0/ 658 683 C    ' .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQROC&&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO@V" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?:6kVf2II'hŖg0 ;/4.J)W,?S5d[$1M̊2ۏ`;(Jgd0$)ٚw?TK]5%{ =#SC>VTӿi~(ӿi~*(!?4߅ ǽ|'aw%زx8=GY6v6w7ڱV8ӹPּ!sq"8ּ~ :̔R0/o«j}r-ݶ"PG}ŅsoITU0Ķd"/RKU[vz?WbǙg@IY"/TGp;_KyK_u{iwñcͳE_ͳE_Gq^m,m,WIm.xv,KyK[uWbǛg@Gg@UQm.xv,yKyK[uWbϝg@Gg@UQm.>ş:?:?«nu]|?Xߥ 6“KVAӴł$m ?/ﶉ )|㱪4]<6ԇK}?­ ô`rI[@8xzԷQu)*O앾g@Orh,B?^*[6۪/, 8ʇP~J9h`NQ*30{R#"hL^?JB#Bhi-U'n>^k ?Xߥ >g@Wd/9Me'>ǵ/u1d`!s=܄)W6K}?­b ʑy'Y۾ >X6kF3=hNj=iE_6Kc%"Tn[ ؜_F^24i NqWpJNܤ=4[6oEh%$GZ9v?JEudxO:ֻo}94&t)ڳ]ȱ8DSd8 >)$tj!ЌhS[ӅՐ٤ |[|-O}?´g]ZIԲ U]5X"k:8@߈ TJi=_MArܱkVrңY:]N[Mi=_𪛨KϸOo6Ki=_𪛨(VطOߥ >i@{U7Qo>Z}j=_VKSuUطOߥ >i@{TQo>>ϵZ/R}?ªnu/o>Z}j=_vKSu&=i.}?/U=nϸ}Vb? c~(]?SF=i-5?·Ιg` L!?J7+ҍ;rlD> 0ҊOe4WAtHΑe^"kHn_s:.@xtH|_bf+B52i6"mF~ƕ+HZ7m$Mx#}OX:?Q?+)eԵ .έ[1eڣ= Hn"oV-ypxWuZ .9ηoO1Fq«eYO+RV<84_ƚwV 4ˉ-Ƨ<&c%0.zVQQK18ؚg/G&>}7'gsm/@IKK5ϢM4baudIG/o.>}7ϢM4}]wKK4dI[_غg/G.>}7\c엿7h%M|??4_Əv1>{>^Ϥ]3}o?>%%M|O^Ϥ]3}oLEh\c엿7h%M|??t_Əv1>}>_Ϥ]3}oLEh?KK4dI[غg/G.>}7e>./oK5ϢM4bVpd9O5xSd)vp ??t_ƚm29i(hiVx wӝuy Z+1o]3}oLEi7݉bm1cnH5aAZ??t_Ɨֿ9!$L&A6ӑR1֙4wD+;ϢM4bw?F w{vx u[?غg/G.>}7l_Z9]˽3PGAϢM4bOգܯ˱| c)].>}7ϢM4}Z=v9ϔw?k?_]3}o^cO|>>S/M3}oLEh{ק~>SW?k?O]3}o^cO|O_ϔLEh?>9߱_ϔW?k?_]3}o^cO|>ŨϔLEh?>9ϱj?h>S?]3}oLEh{ק>ŨϔZO|t_Ə]3}o?Oc YO|ϢM4D3}7ִ*wZkOe4U xFFp(g譿 `#{Oo=Z<ku${+ }ڃlMV ׯ4I<3tq<\Wp$r#WN楬bHppTϰaF25@v_VZw,?Zz[AQ#,lR?+>p5(̤Iʱ^0FFE2K.;?U,l]rqJz[WYț\~3psҮY>?A1b(RQ@,ylhl*޳D7aqF_s+3? %`N{$iOE>յ8FVt%m: 5 c)T2SFA-fud%XL( z|J zqJ#97(4 (ݧ$h2I+}y\H, ߷U&Mt>]}HP;ݟq[Vw4s,ȭp?Q^aeoy1x@8q<kwi- pX+c$lA>N1ٙӜIEQEdYX" 1:KOd/|aFy *M'([x95fdT~#oJA$ &]|T3^閟f"=SZքckSJ( (]i>u)<]A8'ծSm} [peP bNJM"޵kmstEc^_^O=Zdr?*Xp %b|YܓZVRКM%QXQ@ Tm>q qHT6q\otN[Yl?\c!OT2m+F^gCh\'^\' }jSG,gn 9_G*1~( B(Wze?wwR N.u:Y0"\c qW;2dWF}VwOuch$S_29m:16r9t m~sG#y;9Ix=iӌv2;JSI\KIJ(!&Z(Xx ǧZ~m'Hs9qE>`xLR hʇ> H5XS#?20= q@Lnb0Y0 0FjJl3E<)4/B) (()GZJQր8ȧg_OE-NeО譿 `#kOo=KK;p;J]GKRƳB?q+"Mf(7S5vi|1|y>'|SF-:E%U\ P}p2~z{w,?Zz{X4{ w -흗ky{HoLU=E1}!U[uXQcOrj;ıK_V`?-]Wș9F9iS(1(W\CoM4DRrF5<2$1\H0x8Ҁ(H-"a7ݟZ'c7e<*Kylq)g=p=]ʑi݊k ,ⵅ\VEJ){g"CZ;g" F]"P}okyssnLms#&,?06G;uLQӸv+%/NRQ@Mkh?{ɻإռ7oE5~b֝^$Wɝ9=JfhbR(QR4UQ:z(QEQE%(@S'ȧg_OEtV{Ѭ't2K'2hSriփs`bhaHf(rpq"ܥ[PKLFHUVܥݔmi4z\;8GN;bI>W?U"#frĩ<[.wĺ[YmztD*>S{˦:$ {C'o~:޴v{]*x݆ |%$+s̃(#XIT*[0Gqb(*@f:m>$jo5hIj7HO7҂*1Ci-l,cii@Cz iqN,uWo" 0ڇڦxID; C=Eu44s6TOq$Kql;[~|{WMɻKZIqhCV15if f1;$g bT"l0iʥխmlq=79V::[Ij1y?ZM O 2SЊ:DV$vT$%Ys!끟 H B]07CNkU T`*;h0&ۨʘ@,Ve2@ yK,Qq4Hj2A$iaVm`ʶcLph#XrrL$o^+.ɥO+9Z4Ewy *-⹅0 S~9}oL$[y[EDn[$;.p٤riyM4t>`ޅt4`O̗3]1ǔ#F9wU{緷[Wk&~a}cȋ&qA~s~fG]K9e( Nwzg55_2\n]m8OLcVmbKui1$:tx4 rzPͬ걛.1 y#-q85bD:tf-#z[rDLѶvf;Mk4n1]}yj%9kFW3izŴ]^#r Vm6+}3JNfPw Qe`Q``re⺵IH`#Hr;pqU{[֊hXFmwۿ,·huVL. xa,m> {go_jh3vkǜ[Hxyϥ<+Mo}i%wQK3/#`[wYmdq`0ڛT{Lx@XOs}ZLAiȼ6e;vRv{<ƍ Z44Qyt;Fy 2Ij¼naӭd-$;ot'n2;WF 9CHiM[/%[8&6Љ4&h"hd1ɑPNƺ|\`smzola!+OgGml';W#;N1un `ʊU[0i qmy.g2;/Ï>; ؗ~.䷊9#F26ČFqOnVEI79%Eߜ0^U=j{X5.G,$FʌpLa_sa_sr6i\\w%Alq,xB߆jhעO,I6[i :\+=+=Je2inΔ@ṗz=z{UKt:]`B6NI$8#ta_Ə/\U|8>ΰ*ʻ7mqr\S\%h5ob*}>ĿsM[ b_/\VF(ؗ~.K??ռQ%h5ob*}>ĿsM[ b_/\VF(ؗ~.K??ռQ%h5ob*}>ĿsM[ b_/\VF(ؗ~.K??ռQ%h5ob*}>ĿsM[ b_/\VF(ؗ~.K??ռQ%h5ob*}>ĿsM[ b_/\VF(ؗ~.K??ռQ%h5ob*}>ĿsM[ b_/\VF(ؗ~.K??ռQ%h5ob*}>ĿsM[ b_/\VF(ؗ~.K??ռQ%h5ob*}GD,XqW1Q]9*<["~=xE;?Bz) mԻh@ue"?ҷ|)<=khH562ZlߐFzd׭ U8 B0|rv]4卞R1e`rd!zgvE:t:Luϟ sAKJ(Š((((((((K즜|}i)7Zȑ7ȑ7:/x`|OQI?/x`NSI?)|hΧ((Rפq bQ%:9 24sJP7DY~6ޱQB:R@g֌U+gB9:UHU 7SF+RSF(RbkauOM\YWkE|~#Z:wUD dD`vjSo%Aqfrd]=I1[76/t#,KchP3MFKw#lkqnO3\HѤ'W8=:X,B65F%{vu6Wv/+ٺLj =k:M.7Ffo$m)Ɨ[n(7Ll(z-J\6FT uy'qZ[$I MtXzJ8># 2!$nXܣ1x篧H xmR RXmqCdlFZ,ivKUue_r{b[[$2 DLS~wc=jps9S;쏃'4tYd26fb$#ܬ^y%M|5`nSm]DzD%,0VH88ZIq>a1!Em㜕4˸K*3vM!Gqe6q0Yn` #P ظk'{6T YFHА3I1zpqKpQNQf(?0khbu=`+ 'p3=6&K7Wvsۊ4쎼2jƱ{/j1JIr늠{@BXsG|:QI+4SuqF)أQ~(0+guOM\iWgGV֏vvEO$wmPg9\z>1LkEfޡ:=:=:-aguRY,ʼnbN0:Ve%N8>vV$I|Q\q֪=U"Cm%n)ʺsvI'W4'O͉@qМjxk(fRE#WVe@4*ZxPpLnTݍ^uZ5wy6̛Y8m?.)ZOaes2}hcmԚꨮaKYf,CBbc EP0((((((((((((()fS&JI[OCyĝ$c$cg$i$iv^-XFaӿ+jv^-X/.)qF+ɭCx5XZe, 3B1=֧BY N2:(#K1 F"4e)X E$ FIJTIcM; \SF*1O"2+l0&D> .5a |giKIK^yd^k-k(t,H-jJ to10Ϡsk]a%qssNM*;YV[DdiڜZ2y1$$g{]ؚ+;yђ!%`t=日[iCn1y@ sTdf_8uWQi9$~4?G<VVg(g ˏ\!jGiq,Hq?!1ȁYvf[#ƦE҅l(R|\~l9ϭEmY1lߌuޙa;{[nʡJ7NsMP7vgP3, nh[i#oC+|Aɠ Y|Imȫ<ЛtKmi{>Q?ᘪC,=z m2hZQr_9smn4HʪJ\*P۲s@Iq YmtS+Y`ū0B9]Iެ62p%p2H $󞦑4."yʼn"7`c8 skL&+#*۠ZYcfEeA @4=;hDR .$lNÊzm~-1\#kd<~Y [0H Y ) M>C1eOȃʬ.Xh/ ֎DsGo$/0:3[BVIYdD ZKZǽ̉+1cuCuQUHc0! @888v8C-uY張Y"]@ `z٪i[Gu KJ!7=OR__[D\#P4{PIhz)ȳBp2FCGO\Ts LCƼc?:}D34@ꡏqsӱO&Y"8$:{@PQEQEQEQEQEQEQEQEQEQE[Oӛ6_e4@?4Q?4PQeBc1ieBcC1iU.ڐ/k<Q"YGV87mF!1ʲn}6yYh╌m@}GE Z9 ߀Ñ5v×[/kǓpJt}Njhaqe,<@T%n#'%6ZW[#4%nNO\ZM8Gd H ``=E'QdPe3ڝǝcݳyx56eޛwxk,bxao.Wn ;R:pY\m?kC<\WjwGё&)628_GW(W~ .5c h|gaKIEyyW4OQ*BCg;uy* $)U˼p2Hcj~'BW/'yQZkػxl"I ι܍,r3#t ]*=6ts׵D4;` {vݸ®hGBdsg>5c %F_vG 眅#b$Wj6Q4#VR҅'pwr:ԧ¢{hQx3'w3 t[F1.bb$1KSO5hPK+f8q)06ڀ{g[uc`o"BӒ8zU |)<%%Kg,( c i@P@iWZ|S]° 9q#Hˉg&9Ri,>9ZDS[OʋqT5cH1%~gXXSqO>#{x*6H S = իOe30[xG1y(E/]aK8"i$:;!Ð+OE[N5bke8f$3Ӯ.V/NЉV`6HF8\WWwmɶ$i"EE  ^i~DUݭŤgDg ̑m5WpX n$zSuB kʍ +a D.R1\eY'SRGIlO aR|ݎ Wc^oabD1QrX&@U{`b7.2ۤʩ2&#H(]sQ2(V׌P_iZv[5D^S]T|(lCm#}XFT#=ziWb7OF9:Icdf dB}M -GO {qDJ%eQ)yU$DH-٬$rӿY^z] m2m#"WxA"mFqz&qqW 4FବB`'ڷhG/s1܈%3]e![mV_ه٥[R6v J(`xvjwNM攛)J?G'Y4Dq#f.:W,3oM4kV%[y#ޑ;!y/qr@ Z-ԒpGt``ǧz,66(9}ǁ=QE ( ( ( ( ( ( ( ( (/s}즜|}hG"F_/ފ җƌ 䀣 Q.pX=e r3Lֲ.u6KheS6gZ|4iz#2V'<}kEցkp"l2H99\ !I92UkO]I1tƕz+qy?&ݞ!@c>]h =J%֯\QV>:ӫE6$quȩzC$ag*[E>܈O .p-x HqHgN?yé;OTuDXf0>cҒf[:+9 ۡmOLD-Zq,vP~ l&LrP~TjĤoQeUDZiZw"^|Lm&ڗmcb"au5sk>Օw3Z :ZJ+E!&H0w(9qQ=aHJW 9mm-";xh#Svf>TyGS4(E"E匌{ֶΛMvm- lQG k1qEESZ݄y@w}jJ(4E @=:oجS~G 1b4* 0cxQrK.ц'Gzuv"F*CijYZ0,e@KEBv% kV8"U@UBA>m#BќeL`{T*`EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE[OӛEw C!(>Iuchg|z('g|z(]׋1c!Wh_ uG,«9'=$+Ŝb.qN)ٺJu}NgFkzf$x  ڹC{YR1Qz½DՑrNҰY" rGZ[P͔1;qrO`W+2y:\-HSYf2;g{^.,3f V _?~2sVn jG=@eWقa:g^k}QXQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@^i"6@\_#D]ixD3_xD3_,h_ u+8 ִlh_ tx%=0 =8 Hl.WxBӓ1[K#-1/aC2Yu/ܶm.ɭX|)x7LU{BnՐG~ꍠcںҀbJTckJK|2n'96ts߈NbT3>sy?rLZ]{֎cy7/",$cin}ZkHc6`f#L[Ulyfd sT8}Bq!Zqgri7UhbD8gzVO R$ QB%pO@ۈg5Ovw^ZvMl=)⸸\*K>hŶ0)&1"(;cOwͬ mHPdsQ} 9byh;s ְ-.4utdf`#r֌}\um;>m~uV-1#I2eق@'jo &+,So.N>n30feyaS_+qmoz˶ N8?7{hw4zuoU.52pELw5:((((((((((((((((((((((((((F{/?.I"F_/ފ@ڣl VНN{ |pXg$sndt`y$x"DR[pfD>cwYVT؇Y z>VcfRCp}8ʮ8Ii.nNʷ1eUdq=*qNڲ7d$( 'j׏Y-Vxˇ`8ym"NWoa)`V2,}9*~P\YR 7 +q:OX,(^@M@osH|I{H㷴^T pDF2XwXzFs,D֎Eql7\-~l{os$nY h=0xI`,gWkՋgh>APjfRdŴ+؎r1jo^im Ф ow>N:VzV` gU(uA!"E5B~lrG^qY]zu6v H\gSJ/SvvyFX)At~Nm~"S#\u(RU"1{# 8(\;J( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (+h"Hğ$iğ$iQeBcFr/xMhVRI_)q S<21K}3] _wMi}Nw-&Fpx]/~h>ipĆ\ 㠮KO&m/~o ?CdzqFT1va??O&n\s\m/~oL'> ?t v;`FݣkK?00,e,yUH%w% I?VԬ^0(FFN+_i(I'<2m3~hX-u4c3EhdWs^< u}}G _{o?.8Q `?x|<5,z?e\1-֚s\'ۻpۜczpH֢)QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWWtZ/]4E֐7?H?G?H?Lv^-[UeBcľ"l[I?X cZi6ݍ+g@˟hg@˟j#h+5.北5.北O{H;Z+g@˟hg@˟hS=֊E!fn@3esg֥q'Q\B|Iq#i+?.ﵮFԓWҠ#Y!1Ҕ(5$.P^_Zئ비ck? FX** \0h(Y&,^V[8PѰRqq9Os u)dHϜaI{;[!B| I`=Hh(\wXYyӍs@ois \0R`gO42_(N3㨠 tU8u[ Z9t#) Hm#PCk#1uxN22{RȳBǒ*ApqKJL 1NXb1@ hx]`޺)z̲ƪTbE/nP4m< ~u1aV:^N!QJeݵjh)ip8 )]dZg=wy] FNcm;bRmP6ѴSF).F5BWMzQ?Ɋ7iZn 9>[ +7ݶ 򊜌7CWKdg(G%/fՖtt]7YQI{Y^bAY߳ 2[<멢D.PHP4ŚTmZ9\y=#֨xbK(@󭌪C;=95(<Ixjk !#k4AI$dbtHĊ־f#' `Ͻt4MeS&zC٢HRT_wZk{Z@ѱz]e"!YI@=(MS]M^Yb 3\/OQ'mU1hsr:%Hr$FG_j3⇨ggoԜ,/8FtYHxM}<]c*1P@#;B}I9ۍvLmYxs=Zf{67Ms] 3FZuڊy q+<Di Ka CojV f8-h1ղA tP.ai63p\"sS:Lhl.[RiV)|ɜt*{t;~z|:y@si2:Mz[@Kc' 9Cq]jzwڅO6=UNXqK@is ހ9;O Kİ+?1' l@L9'I'o$lti:,"!|mpX`]fٿzxUw38Xg/m˘K[I|+L[(Ipg<խB}.Uȶ;Y8>ơKJ7BXsNfU\=I0-F *1!vqǞzTTZΡk@0*prqu\ĚO &K$USq},=z̨2{P%R-3J\K @ O_a]0Rq3ɥQEQEQEQEQEQEQEQEQEWWtZ/]4E֐7?H?G?H?Lv^-X?O+zv^-X_?hhΧ tqK.(%.,ۓe%Iih[ڳ64K^BN ֨-[YncB.ҭ閑| pT?Z*˚M,R2/ n/IzY@q]f^0P_kx2%D D_AF(wb\Q`6uQu&(>S^>ykשx+E+m\чݐ h.-~B¹|o$8UƱ!^qTVe2a!;Haj"^Mh4!D;6SWOݭ ;b7rv+, =۫eN?5 )F3z{tLsrG9WKa[,6gX־"[kGH$lFC-u#>QH9!YUDn9Eb~o`BmpkfYDqIhcDž;gpNPqֻkEm啧?߳~:|UجLlyA0q.93OX,?e,>vE[I{g~&*8,qVK\]]O=GvF;gkQ@p\x4kvnţ9@hBSsLG}|wF9]mXgi:/4۩kVfFO=U٭o n&-|d0VS DxhV8CMn|?CedWHݚ'j?Ŗ q+oPrn% {e<8J٢86,$. gBT* ƹ -ݓ\A`?3l::ܢSisM%ѴKyB:_1Zfimy}'Ba@~5@XoSq\ZM%dN̹o?Z"3o5խhI q]&x[Gsi4ٲ1,#TF{bzњ((QEQEQEQEQEQEQEQEWWtZ/]4E֐7?H?G?H?Lv^-XeBc_#0KP8zq1@H1I6pMz\ޟ-o ko 2p}w@sҼ\.^n6W(q[Icta'U^ۻE7<އڹ[K$ ߥtVCo 5cq\}.t"mF]C2㿯Rv:0HO"9'֒A1o0j7۷PbbۜwQ%wYX.Nqң\m v!fu 7O>{jw`?HG,dv;$:́jI!pxhnGi<;zHk[X4UW⁝ G<[@Hă,psE$u߼+־g52G4S*¾ qMe}k~֓ 7\kZ\ygZv HT֛q絝LE P̮r0 ha=46) -$,F оN?[s ,aA(q߽u4W#>wrMrUm3a~]8%Q@Š((((((((((((( "EF'g|z('g|z)YпſG;Blh_ uN!Zѕ_XRbCUڹ"lUeQ.wg<+8MIJ)6iir{p䑙2}r;aj܀VujdK=ޭjQ߼.zyugKBQ` j֗;\"Kk c'wg%Y1mUmojCoHWd@u!Iç8G,sy #o=%ήZ].w6\JS]ZMIJ- W  rP++JQ[$A8z?XCg, eB3i`g+yj-ʹv i3bBNiNue' SR!`G1Rbk|"64qץ;Ekm\8mCW KWn*+nZ$ۼo97Liy8n0@ҦL[QӅ6G{}>d^hֆ:|H`Ĉ_Q(mzH9@5Sy\g?.3Ԏ)twTKHr$` {}w#$ ~Balgtqh,y{@ݳ Rn [iⰊpe˳:3=naⳞ&<1S~9xVe>m~a, ^vApzSEԯ%k$1 E2!RG'gɫjigFَ ^+_T}?O{qVgdn?*Ds`I$6 7w{jZlySCuĿgK,fEߊdCjۆf$7>H}ӓ~b fxy$ 6kG,: DE02T㊕`u(ErႤМe(lkCS@XK1Q68;&Ps6\ʛ6)9-愶=%E y5ȯH!ե/fxշ` ujs4Hen7QkYe c=."Vt/) ûU}h-ӌ[I<{T+ #JYf.^@T `7Z~Elc2 BrssJ",q,IbA#@[l騄^ZȸE46o-KB 2 =@P}/m>5yWԌ cNhX.F!&  @U3i#EV2,zOGqipG#y [wr2 FxhBڮvڍfڳa>հC(e 2@(aEPEPEPEPEPEPEPEPEP{qAu\_#D]ixD3_xD3_,h_ uf-?Bh_ uV!ZѕohR$ OcOM gFEz3,[<.i$W6v@ O<_AȅD;I8[zmcr XJBFG0Rxfu\T=Re=7N 8}Pr[w촻@r9H#Pr]YWsZn+ab|5ͭb$i !wY#w#[k}F)bRI xE3p9T7Vhdf6>R8+rCF$k)QpMe+ TcߌTZ>зFqW7H݃^ZVslIK.⡀9ʌ֓C"l7!#5]Ub>wavrb#)%dYce]8Zu#LTGL#)9zzֆt S4A8<瞢 Z2p1YSkIźVFN(;TFb~(0"qȯe \|?,Y}Cj|(|L]CD{nTRKt ɹI'#>[wq݉.xDU-%~g?!Q]ZniYbjSo%Asfr$#el.W`RzWC.9/ Ou$_-qL6}/}l݌=3N5IF72$3W8=:Y{IsDqss c|>?1"s˷x,Jd{In=S/U񢵤"V#, :kMF)7 1pnN2[F*Ee'G[9;s"?w:ME1m46kyh$>sY+)6- `c2 #ܲBhF2GY\GItg؊ȥvvx<Ҫ .-"DN@b#ME0ì:JҺZ6u*p7m{tcVݮ!#prIb(S=SMs 1qB8?*V.ZO7I˘|[tP#\saT[V-誫j.p01{TP0((((((((((F{/?.I"F_/ފ]RhiW'(PST2% $AcO_@a'v Wi#UծM5 $#('Χx#\} J9X-މ}QE!*=rMH#[ ~(ql|;ګ^^C^CpTTVNx]y+ &*LRb2"a_ ȳeo מסSE?fq_ :p5L4UqNN"!"F*Nɤ&J(hɤ&J(rh%4dQ@ FM%4RQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@]4E֋qAu O4QO4S]׋1wc}/xgx]kGFU~yg_.@9.N+)HMkd!ni*j]~W~Dn'ӡHmyP߇jbKx 0 X Vl 2bWaמէn7:nP+t>)n +L{gW/&6hVdl NQwE(}a$H8#-dD%W1#~f̆f*բ Ȯ g&dҙ qգEAs.O Dm֦xsOϵN_EAso 鳇<HЬS6zz֕Y̙<5<:m c?n?ZhRq9?t{Ga.йZ)=]IY>Ӭ-KpVecO+yJۘjEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP{qAu\_#D]ixD3_xD3_,h_ uX_/o:ڠBQE (((((((((((((((((((((((((((((((((({/?.^h 8oȑ7ȑ7E [Av^-[TJ(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@Q@Q@Q@Q@]4E֋qAu O4QO4S]׋1bпſ|GK%EnyŁc)(f5t_:/µN~Ҋ6t_]:/Ҋ6t_'&;h+{COQ (Nh'{CORݟCOQzù\_&l] OM?KT֊6t?)·zù\o&;QzùQ\x)Gn/—=;uau>iiݼb6vT#TΜʍHbOQq)%!?* >M7uh-ն4rV'8ϵfhZKik-42Apz9WLG9l26ԓ]} tU)uHcD9-9Ai"[`&WD**NܞFxE*z fW,7*Q4+N-I[9}rqqr1/$&u(T0VHN y.QPG}g-Ėۼ ʥ{TiC$6m@gR:ʁͩ -ZqP]k[wٶa@BR:(BmvO j&tF|O<ϹSlG4O2(39PlJOE*DH8g /wm.hJ#H"dU;Y$uZ_c%ݪJѣ`88W2Y"kr$| j*ޢzY@ۘHurGz-jș͌.0shQURݥTfwJLk>ƗmBĄq2y> EUR]B\F$ HiWanKpҮdp?JO4ХK̨%RяVdT6ew¯ p?* ( ( ( ( ( ( ( ( (+h"Hğ$iğ$iQeBc:Bпſꟍcy9@Ի}PQS! y $F¤`sޢ)6aL>jMD;i6(@mUKlYҔU[vE]:;d sHU͵uhΌLC#8Ͻ\O.DSЊԽՉk20* ;pc@亍j4"H+;UQX>PNJ涭53*PnH O$`c(Zo|B$͹vnU9r4nt:iwJn- N N O ZvO64pcAfq_ :pSj~DDɭa'~UΉ3^$u42Pb#sZXGn3ޙǥ?,v"rۙc%v>dg'7Oj/<4^s1OWkEv֑5±SnuE'u%fmv'8lb4ϒՋ&%0:vXмGRN2I#E0o|5J4{U>c ҥ5=$,y,PҭzE%ad~V 0ХKv_KQO +sJΚE`umXA49\61?kDhn![hǰZB/E*ڸwy[;͞+IY 88?:Z Ȫy?P0ƅsͬ[<$Kr i'5)tykIِA0>i"G"D\3Dx!ۼgL9ZIk˩sqn-|؍00 $dFHҶq8LJH:w7C!i*2@Ϯ@ɹ&8$$lp'~P:z  3JfJI68 15z4eJ$wu'b,~yڿץ5u==o["AP:$ˤrjeIg;GAJOK(ۘZtlqUX2I_95+#%iVII*$!wP=sL  4"IUe$y7`~)ϡjne3N"fszֿ7l죬ۭIssoin3 +Ұ;o=2b5K+ @m5oLњkY$眅BĜ 0'YPHcy1)eZ}ݭBKˈBpF hgO5!*V$$b)p9*sK{*E b0[g_a{(e\wH(‚[$څޡ%ԊBP9v5P=\lpHˍ=XQEQEQEQEQEQEQEQEQEWWtZ/]4E֐7?H?G?H?Lv^-Ub27fv^-Pe-?o[P"16s!x Scz<ڳjbLuyml2q VL輫o-ToEO٫#qdּLjqrk%fy(cEbHwj%U,# <}*Ver7zvW-At $G$dƗA6ZFu|VbKHj+'TlRai$^`|6D9gk$V;F-pJ6mc2Rq-BY-3~etG>Vm$ie$kk:F aqΛe7V^rMb@f|!pyF_j:h/<1;pHҬjɥ#4w J\o`bдIr1,~tɮM NIhG9TkӴ"dOXPzqWh lvI<d۰栰u5HXb2H y5E[0 Kkhдy dt'ZEֱZ||c;rF$lgEbܥψ!Yl<ձrڍ=Ў0*ܨ-:yJ(MM6XI}˶EA%r J-[2-ܶZXw6\ ҵ%oֶдE ͜VmⳚȚlYX$cEMP+{? J+%yî"0`w~w_#1< 8NErh?!dVk6k;w 'o+4RPEP0((((((((F{/?.I"F_/ފj#mnMS A20:`B;:{&XK8_^^[ Kv~\t6j)3V2J]Fxt2HJE*?k(SGt1bC9bF PMO㊞5NOb?*ݭ sԾtc(6A ֻ JfF9%[^uv+ /FxZ9+dg?ՌM$\:lPD*(rڨžݵ%J,%9QPjZ,z徧уoU@J9IcT;TLB)QYf`8h?OJM:4Sub,X?%ސ5Zh㕚W[}Ѭœ>xwM'Xhq.?x\iLԡKSit7oM$`py=sMÓypAb dCAc|i.R cYf˃9Ͻb]ciZ)-1*&H3s K10u@0h:]%ʎeh*à*I42eQ%.c>k tvM,HD0r6kr؆_-,X䞸@ /&[7~FW Gu!%kU3.r8wlK7V %0fib c$8l yxkX|LVYE+RyM#8 [hnC }KVKr2 xjMRO,&q0s9gDv:0[c2 =0)/nᱵ{+p2I'ܒ@sɏC` #֞b^TRIc6a0ȠBCۼM8# 1_+NMK%Xo-rQҰFܮ8m_e8l_8'1(αW, m%廖O^-@g[smA*ϦsL2[GY.r '-<7rq4LsL'_<=t٣<6{>-r: m)|#?Ph[QE ( ( ( ( ( ( ( ( ( ( ( ( (+h"Hğ$iğ$iQeBc~7{HlH;8rv^-U`@K<~fӳ5_- uڧda7&2殪FyU'R~xEQ)sn*yd$m3maMMq<7??4@@'$ Vv/RIp|#9{vmpv |n--rOqi[`TGvFE$I/5~R?xw90GYofd .8\ sk݈yw2\ē-,REn|܌5׭8lꭌr8o/S;`'GWpHI$ii>Ku|bˣ pu:ΪC+6ڲ:,I4A\џPeNWf6h0 t㊘ܸ#qVGy3]!8 c8w)=Onj7m 1:9UMUnH*Մ~[\;D0OΡ:t =1.xЖ44ٖ)2Iy5>t YÑ+~"[d\6zOP+#1mYٗrWR<1#춒|\?9zPXkZiH㕒~eFV†\w㨭ZRs D2KB$*9&ha"1APpX@'B]c A*Dr% dLYGsy6 Ğ]r @`G9oY\yPħUFgcP`⛷|t@$s]wmQ#k+,n7²QߗSĦfUQrO=.<'ڇ s@!\K?:ܯ"6yLGu cF{[F3`&ͬRcgrz.\_1,N,N@\,@@<}Rz["ۀ173 wP :dԬfhݧf8db͂䎵?4P^@Ion@,JrGNi-2%ð *!f$dmP`@' h}zku:)`h> @gUnE̦Q #d;dbrX 09Mjqj)VșA*pHۥsr7S_Vp?bB*`s|Jk4. @rۜSTVd3/TFŷ݌v!d2mFJV*9-%&hv!ߵq|U/pn*FUA'ݸ?#Bdc c$@IkYݪn]U|Qr>QQ$HWPX''zV}0ݼb@_QSϬ[\9eK|*z~tUӮ! ˹)`v2ƥ} ƳDj`[=rޕFwcIs$I!E+x,Qd6GqW/ d =0.>e\Ky}9#b,=bY"P=>%Q@Š((((((((((((( "EF'g|z('g|z)Yпſ֭ŪDII1~JeBcw(R~(\R~K_·s;_D ߢفG"vqoEO ڠ ]\sSφmQ_V<5jHTGO'+z>CPY> @ ݵ+ORO𮮊b9?A-)w|R@:jutQp1w|WAXCd~wOoZ  Z-Gϒ+# &>Q;Am"rDH2GUX4M?!R\7\s^u $µ,hC|$$=y}M} JJ22lj8'%~'oBj88;uyzli]G>Hpo^3|nInnUc#9υx'[o2DG81ll\rs(y6x, Kvn$2H8 MDL;PjV^T5Ki;"Q%?@QQ7CPTh|y%!(D?oIEG >?@QQ7CPTh|y%-E >?@:Q2 2?G!(@-E >?@Q}7CPT_h|y-!(D?oKEE >?@Q}7CPT_h|y-!(D?oIEG >?@QQ7CPTh|y%!(D?oIEG >?@QQ7CPTh|y%!(D?oIEG >?@QQe;#R}Qv~FQv~FQv~FQv~FQv~FQv~Fv~F{/?.BV*r> Iڱ2[O4STyW _Gu14֏89_6LM~CmGZ<\知u14yW _@}hs~Cm_6LMt~q>yW _Gu14֏89_6LM~CmGZ<\知u14yW _@}hs~Cm_6LMt~q>yW _Gu14֏89_6LM~CmGZ<\知u14yW _@}hs~Cm_6LMt~q`dgi}{W=_6LM~CmD&<8Rֹ?Jchn&?&:?8Z<+ۯ ҿmG}kn&?&?Jchqsҿm<+ۯ >yֹ?Jchn&?&:?8Z<+ۯ ҿmG}kn&?&?Jchqsҿm<+ۯ >yֹ?Jchn&?&:?8Z<+ۯ ҿmG}kn&?&?Jchqsҿm<+ۯ >yֹ?Jchn&?&:?8Z<+ۯ ҿmSϰ|ߥc7.H=Mm \chsaa?;Ich.&?&aO ?Gs14QVs14yO ?@aO ?Gs14QVs14yO ?@aO ?Gs14 {*kK.1T=y 'OkS'ϠZZ_rc,Vcēw4Pfotoxx-15.11.1/data/images/view-metadta.jpg0000644000175000017500000016215212616075370017134 0ustar micomicoJFIFExifMM*JR(iZ023090100Fotoxx:mashup| Fotoxx:mashup|trim_rotate|resize|tonemap| http://ns.adobe.com/xap/1.0/ 8 1000 1000 2 2 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;D" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?+A {S aܵī3ҸIuˡv`̘?'ڹn] 3K33޵ޜLyS?Zoebrǘ-ϵks$ф'пjg*uvqTSo90}iO\ȱj<*p Lk#ė`!W*q4\,t~kz <\j:urV!ϐ0V9I"-|[{{:h{c;ۉYYr71۷担>yWeH0Q, l B~#fn),C}461cQA\$>+״V*36bW$Xy0C\"(FNU(mq?ZO?>vN;R6q@;18:տ{'Vm ܮ̬ݹ|{~lwm{'Gٽs,H`TwQyk% !v>쟕fOʋͦC`-`et; 99-+MR:V]1rRF Iv+7~T}?*,9Ѵvsd\?ҭCm~~`U0ZWFKbsToӿFnbLp=L`BK+ $F43ЦW`^8桴4FeVV_1a~U q /ify@BA6[_wS 3zEZGKQjD yϕ`09ڕ KHmZypn#O͜爣G*Fی2:w32ݭ%u]Lsoh IgB?LF^53Nd;k1j! v2jo,nM0scs'N*I$ F&Fot*=kO?*>=s3?D;.(w; ,(yy>{}7}<,qP2~\EwGW7vJѶ <ԿfQ`֟ٽQ`֟ٽQ`֟ٽQ`֟ٽQ`֟ٽQ`֟ٽQ`֟ٽQ`֟ٽQ`֟ٽQ`֟ٽQ`֟ٽQ`֟ٽQ`֟ٽQ`֟ٽQ`֟ٽQ`֟ٽQ`֟ٽQ`֟ٽQ`yod Nj,Cf|  )r7F[[X% 7;^{CkKrHgAg dv=wV,|xRVy2n9h'cėR%y?2l 3FzS|AuyY}EdvfO-sI!끑Y^'\-(KC&N%Ar+׼E=0ktR0rÒHڗ-w UE(sdx< (bI3Kd`Z A6PޠO(( zl֏v3}:4KԢFqw2pp}}ԍ:{B8l^FI ±ԵH왢1R&Y8@cBuzn-_.a>ENў;dvoqj+e I!` r'ikڧ㺆Y7\Ilv'k֪]R3[&#Fxh#AR$ݬ@HX=^Ѣ/% 8he;#9G7^0,t5n\ Xv;ђ>cT5=Tfium -Uq6Ӹ2i7P=No~_/D7QA [~q`P(n-0`=;}5>N_lfͳA ;xsY#^jAp;7$*p2xv]WS]P=Ԍ 2MZuԗPq/R%RMpumcv)wqJ%{nz?FMf A~1gqc^z3]gHS&Ӱ9lVO/ٛRx+ac_ ~7b&a,j*;p0 <Ӹ[}WII&Jau>3S꺌ZNq}7܅ c{rׁo./ Ӧ`<[Q=㎝h& {-lLJK ^nTKVT6CG4d1Xduvvqqak# 4d$~N-um:O{W,{n`@^ WZWҹf$yxY2 m^, IMһ+Xm Dm83ހ&(((((((((((((((((m4EPn hbpA^O ͤ cݥ$銣-ZN٥4M@+Ee]&mE j}w/B'(YGqjhL3So<a}$ruz+ ̳|7:y5ȷwv6 i"d23=)%Y JShnbVϙDQ?wך m2V5 98u۽}(<a6h\ݴ  ( ()\ZΛ-r3B[ki+_k #.K0Fcˊ\[xjIn-h R1 =q\6T_S[ Wiəbd`6m#v豮qywKulJ'n'o'^sxc`f)h3KbN c:֤/vz+j |d--7quwgs3;IdbrG?E$z|55. *W-`3Z5:XK2Z>rɌ0F=ix7pf[DmNqм7f\GM-p_\zW]Ǎc6kx s֡#!SNGVd-/q/2< (鞤YkkJdC}UgNTN.ɑ"S {'W؍W>K V2:UJ{ kֿni Vap>_Jgm9Iچ;ilӎ߅Q9ͦ:cwgT''.@تQE]Zk=b? .G8(d266\$a*bF̌C?iޤ;V[Ta E-'!`Fs{ +iiGImCT*ɬ_iMHd s;`€7mj WΦ1"H(ͼ5^PpG$3zԺC;hFH7r0 Mi;j 8mdl忇<{PW.c vrA̟rojq:-Owqg%Y#T+/8\Po(yw8Ҁ%MJKײAH :cus-Ȓ@Y;rJ;-mtn.&06 ;cڝKx$.xJλpŜf;J&cp3-^$[_L34r >R[ˋ!|Ny#A^ӷCC5DHE^z@m7P,8ʺ0!ۋ[Vn."`p 5 ?@XAYeI0f$:UM .#ĐڴH rާa`) d [鞵jZͩ`Wvm XY0]ip6S (XqPٯ-m #Y#0ޘ_ u7V՛y>[Mgaw3IKmgz5m|)b%R ~PuEPEPEPEPHii[} 2/b"V( wX-.Α2^̀~&ݶlY< ұ e_[\[q}-.XSv 3箷x/@HePT6SӨ 3Jp.U4w8$<{h60Yqq$qާ1sJ|&%XV#B3HF9njo,=fuP7 '_oKYc"Hfy# 1=b|L4c/ oF#jKa.RH qJ.|=uq&F0U"XШS˒3@Ծ]o%[2t>1z]˪`dV+c#D5V$ݻ9qf^ņ4Y(y>ܟ΀:(1TF[۾F8Qܚ]Oum-+8p #+GWӡV+,26SB+2-7n܂eH9˭WG1Gu02TAW rKXșHGYMҮ,Ftx؅ʅL:TryO훤 Q721>Q@ u=f&f4T{G_/)x'_ ' xpj᫻[њIgI]}3ՓUQ3qGd舤(P2A΀/]J6)*T< 4q0G_ZخRWjk]> JҺ5F 23c,%\Kxj՟4kL]١eYvH~| fWu#h(BX$M/zNLnp"Li#=:W+I.KyJ# @s(h677Ȑ.d2:^H`A6K87YaZIk>e1Xohgո&xcs>iQ\֬9!{7c8'`3]<^Yc~8Ea/u .M@I,Q6_\NnyF~ހ5?ZN5L ]aXmcWm.ྴ*FC@YQ.X*ʿؼ,ɥMd N}tTV8ge e\zdf5\iode v.ҨďcOj(-W`ִ6 oYW߯.$6%i Lm1d騮vgP#yRNHܬ3OV F@ˉزg GZߢ Xˉ30QRLpLnPV^IgkelW2ƲI@U,I8>R'J/ӷGw*G1i@pz*h{V-Ɵ\EMod=܍.P䁴cQY_#ɥKy yR brZW5m7 ,[Ƕ4 #Կ!u!Nxc$4=R_FD"9%p~Zբ6ub=Ρ%oJ='ۭj?Ǔ 3$2j Q}Ӝp k&#ؿՊ(X3-.ME]ZH^|d >j>-WygbeVppI=MoԴYӵ}y1if5 T`l ڭuomIn$0bq4mVPX%9E{=䒻I$0䞜T7^ncg:>xhk9,TBfIvgwLQEQEfxH][O+gsY1.X J$Q&T#K峎zV޵2AܼQZ.;tW1u/Owŧ]wS$.Z0/g8S@xK2ꑷ٭MlÌ |<չ){2=,/Xwz|PvڭHZiFn J֡Ȭd ,<H? UԬMNX!>g6zLbCDVWVr*`\_嵔uƫش/ B-Vs}~[s둱 e,OWRMZ_C,İ(_;jwz+:uH綜C sˈՖMj;Cր;='sw_ !'#I;q~+f9ᴎ;L`MqQO{kmPBЏ"[u棋źRiPnч$xE-EjFYe?0֩!auowT7j}1mR}SMgyey"]lUꚅcjK=B0A1 l.+Hom#y`3Tu {4ߵwP7Qy?tG~9WI,n,wL9 $9#jw' ִo]XZ4&"$8ր/ Y hY,&nw a9lzqXHֵ[[5633ARzvC4kj0kRjqIЋ]j n\vOڍ踑HjH 5/{BѤPql|,O9Hryc$>UMS^P?ߓʀ # zyf:gIRkYo>uh壗 2 #<sᛣZB%du`9=T]K[L-lO68tģ15֏0\iK/!"ܡ 7;g-FDø~\^u Pkxn4 $+W 7zLV^$ӣehxDZMR?8lƍi SS8Fa6*Hݸw;֐ct>ycק*O%!a[y52n.<)K755kX[(HpZ`$vz_U9L`D-n uڄLP[WD$K{Kg#z+*[OE$I@1YAaT5POk mvJ?@@jHKMeɚ|lnC{3onPAau+6⵺0M(-(!7dKkdl@G\ME?G9OExUAS< VaE ʨ?Z]VWp4,lkXV:Fm-op!QFPOn,zgQXF6DH\F23~ڪt6HpqUDuvyot>MK\q <@O :"X9p*=g@{'Bs]ǧ\^QwV%H1$,8B? g I@VNJy 99ԝgFZ턓x nlu8լʉ5˿/Ӓkz.2,3޺d4 IqM€+j57w :H@ؤa*9)㶞e!Lq.pGˏj?_wFOaq`Q9#*Kn,\)*k'V[hz^XZ]4m* p;?ojvzzGy,sfEu ¶~PvkMZwn>LMuŶR{T+4ypF_JуRDY.#2(VRdPpOȮ{Hm}cv4Qs0z0=jM3ڶZL.줶x_)n9^W糺sk ̞rʮF0ďzEMZgsɧC&4&rH881@/uȽ,\|:Kjs#mruz=ZZu}1rSR_8-wϯLb$A JU"ڣV& /Sh4q\H#ڹI$"*$d`ggڹúŌv+uyg!-;al3.2䞜vZu;+9٣]*_$tڬZjvK.S>G^}+To++[FcrG}jφ|9w^O 4jұ. k^ut^Z;tRq8$ 4o/@9/~3#^ՠeIR`XiYt8.g9TlHv3>m{%`L,̻XwIshswg3 Зe :õ/$ Ye*Q>np5R_jMe_ZIA2:#c):[kBm/l#pzu橢]-ݤS͌G#(fJQdM-,c,J0N? MGB¬yf v@$.u9#P `qSERߵWvtKDnEtJ]mneYdY!- !B8dCBG'ICΎ (ND=_3Ε%\ñKïcVMgEb@ͳs"ޣkMZK&N{M5-dJ$g=I4OkO*08(J]?Ny x27횫shy6H#{/jmoMn37=srxW]{9K&`80$r9(uH[ZƍM:Mm8i gt[ȍo_R dϨ#ޓw}siyc-Ok0`A:~ iCt+cߊ]W@mmlkm¾J'2Hchx:48$ dvPr"շt󲴱B HNhQE$?~OKQCԴQEQEQEQE{O qmw-Ѽe)KPMaAܮ:^{u~ +e R#9]V2ڟ (c겦,3̫`0 ;H\/nc^,rɲkk#ߕ$dmWYKMq(hMoIe sCD?қoGr:j¤o07s߳ik Տڜ˒WA䓊uMZK.;EsF ֶ 1`GTr; jڴk(Dx ]z |%4__>p&n-X1L<ԕ:^+Rf׃Pk7CyKߝOLgy]'L.k!PN:vK Yt\H}[Oր9k]j_C,W$F FV_uB^KO1k,I0 &KA&hFID0)U\ qJN6塍1 zrx)zm %"|XrN2+v}3O͸LH"men#%.?Xq.aAZ4'Xqp@4'7I Y$?*37+M+NhUh1ABNIq@ҴytP҂$a ǿ/CR CdXGyٷ W|ShFgtiD[{b# ak U8H_Lҧ(47du pxQm`c'R rX? /ԥziyi#.LmH/ԭ{2La*͜`:Ԛu"[+wzJ)a5Z鶂v4@zϽs-j,9(,x*^4Օ̲e\4/LvRs㱮; 8^;Hy> Sf};RfՔ/2x+xU طV\50wcO= i]P,o;)nmEhжNx3]2Z$kb枺fX/(ܣ,CMn \:ӎ[Y~J-c>)o eYJ̻ˎ TV֖qm V$ K@i[} -#oEQD_>-r0-_Kd[, FZ@UGj;]gS q}9q)(J7ǽuWa+)!>q8Zz∬œZ=Ҕ <(_xH6Q=T/q#ߘU!{}+(( zk ip'#p~+_jmӼ>HVllYgl&2OO[0VnJ*1l~qsYzP^77,PAx#o;`}xCR>m0ދIEQ!J1x^OҴM;o5b0 7r^$[[:jeV)pzbo5FpV$ @ 62NXq@[i-yqgjC=6φBKkܵͪB6mg&>sC,`ho,X}6e~?Y~OgYYH!B !eq P/qq+;2mK%ۦ_iR 'YD ݥ\LK:ϟx&_*!338h3\&#եҖ#L;m@"Ϸe~X3~yTE⫷GXMxmpf\Ld/>E͌+gv<)?̸!O~ uTW|I&m{Ek5>Ь7 3ZO4om4" L1 m=1Jh=;YnmȄ6qYjڵ[FIkt~[qQOZhoP쥿.Aѓc$DxUkFN$BDlGuWuVmey=:#p®oCM񶪷m2L'0pT)#@[L;A"OO(R7Q.e\3$.9ʵU[bT'I$|ʵh(((((((((((((((m4EPVcjar̆Nv(:{*OE#f,Εl`W2)b0O(?3[ץ j9(زg|;^EoΛla=ZTPd.}.I2? VPEPEPmCMml FGJlN.t=,PJEg6tnsú4qYɦ[5$(0h@sFNB`_dWj&ҭhG+@^4 -u ~>[n3G3V( iQ'QM>bI ݓhд[.x;Tt [.ܩ، ?46uq J= hQ@};MH,lI~qEl&\ZFf П~*[O[[Um0NH >` s>5EeK} {x-ҭY3ƔcC,t|v+R x~GG}&ٚ0X U]7֣x-n'3Vt4Ph"RKƑ'Y((((((((((((((()[} -#oEQD_99k6%e7$cW%ϱIPPNb_EQHr4T_?A4#t\ )e`$`$qnmN|(|:<:m|V,c$cަ|FjM\ŧfOEEq+} 3W`rO,ߝ=xNⱡEEBr{[^^CD/˻=X<:ļ=",k,qJ 9 iQpjA"gʋʋ^_/ml2nWn3ޡ-#L:ԇ\,iyQ|tyQ|u&G7}*j6j .bۡy7 ^ <:<:˴״9/㽁ap/*yT:,d>jgbGYZ~e{Y]I4Vy!P4$Urd;QpEEK{k-ks x&)c<.'22:<:̢̣b*/Ώ*/Π((Xʋʋ<<.'22:<:̢̣b*/Ώ*/Π((Xʋʋ<<.'22:<:̢̣b*/Ώ*/Π((Xʋʋ<<.'22:<:̢̣b*/Ώ*/Π((Xʋʋ<<.'22:<:̢̣b*/Ηˋee <:Q5_̩ |ˏj.B<5%G/_V(U ſAq8/+M[gq롉tSVZcƲ\sR3yKnwc]#j?*O֏ʵyU'>y921#g9=}w\Yc;׎6m#UYqcVwe4Qw{:δ~Tdo?*5A+7:w 8Â@>Tv?ڮm؅ܓw8lJ}1W[F=GF_9A浲/?՜'xNSlmW/!gR@Zzk}#QQa>ԴnLx[+ps8? ^*[qm4Kr R*7~}#QQf#M#V_ 驤F&U&7*T~h5c`[Nhb>BJ$m#QQ\ ksiV6Ki I1,Ia>5M[X+ W7ۤI`m*FpH=GGzʍCC𖑨iz[!]?Gzʏ7yyco?*>~Tjyyco?*>~Tjyyco?*>~Tjyyco?*>~Tjyyco?*>~Tjyyco?*>~Tj QRQQ QRQQ QRQQ QRQQ QRQQ QRQQ QRQQ QRQQ QRQQ QRQQ QRQQ QRQQ QRQQ To욋lk;f}6҅2* ( (9m2du63 UH}'MޏAI.%ʈA9!y=ji]z-DR dZt? g-\Hgd-2 ryY/^{Ef]ѱY€r:r*/սޟcFn4b2xRvvi5 ö6~wsw a[ _zmi/7ZȊ:ד穠 V6WZfl"X2ŸC27MĊd[[Hݣ.;r6qR6^A%Aň89RK"Eg2BTc T!'Sz2Y ܮ&3n4_Z[o6% 6 (Vt}ʓ|kybOl38Mͅu3]A8hZ-ji,-"WjI tx XED1qPq`4yu s9qémc"{Kw]9ڹ AҀ*[xYIwЯ"HDTs|ӚKo˫xwPd('vJU.w] 0[jUt:Iᰴ B09MZҌJ(ҌJ(ҌJ(ҌJ(ҌJ(ҌJ(ҌJ( 4}4 h "Ó:T>Sq@QG5fEF˒A֧` zr^uYK٥tD"paH5;46䅊2ߏJqn.nVq3#-[?b]$3I +jk OPxnZ( TWߍmb/f H@ FA16!o2>L Hr #I&:rsހ:J0=*iZ)Ж*96UfXnqIfT$} x`zR) 2Aj:ZlKGSv4oҌJ(ҌJ϶״A{;SqjOl5A1K9 oL x`zQPAx%6oeh0s@`zQE`zQE`zQE`zQE`zQE`zQE- >jʼn93ZǼY~4_60((Vr2imbWh+pwM刐5c&@|0GoZ5MKO>iM0 OU|Gu-̖Esotm0  W#L4mE"$ȬGjIskZ- ;(9"g_E9.7LR?8sک^Nڷ'z5^qn"Q 19nxtw,fo8Y$2Ƭ2G-N ¡a 7 u`kzN@l:Rigˏ.e?+ctڅlF8dΑ>h'$yS4-/#?2Kxx]۵Y-) Y6p%*]nݕ#sZZA#*[`'-ׯvKK_܈ch! $Q֟ 5ۅ.ܙ3nsq }|Hwv6yjHv9f֭xbIU(n&DUAd.&-+{anvŹ Csq]~*u!T@`ܽH?x{sT dSfm)l!m;E&# mBg\uOϾnx9#ץ5YO[7i 2%FaIp*X|UhBHmg%Rw>tVC5T@r 36v'jxRHbiJ~@5|BlI bR*HRweHx|Oo\o|XG8 Eb8/ݭ(=ØRC9nY閌TkpuWX覀5hfU_{;mgAKgl3JD5'UBQaX}uTqXZpt;jhU'%xԪCu#jq4 7y??ʲ<'"3ZǼY~4_~QVfQEQEcj]q=[JeS- 惥 ۝Zyi[rA'k;ǶB%w-ӒT{"Y<+ 33,XB;(qhjM:;]4.E4#lqSj>[g)bHȣ1O#MN6㳾r1J1g!eUn>hCha̜Q~h~h~":jw.Ѫ8g`t{'̩]CrIo"'5ޠSIȤydd9[ߛ_G MMkvD+e6!q듞<1d/ϟwzڙ$wm=,^[_\K9i_-ܨ.{X1\^5ƶmb3 y.\`" B#x7^YJb ݿ ߆4k N(f2$r=k((((((((((/D"ϘOzMSyeC;Ktީ@GzH?A}_®J!wd.$tz.sht+}kiTw9rO8'juׇH. eA0 SkRYbB;Q4MP=nXfIuYX*c `u)@w{b-*RP'i=ׅd{v[۷˴?Pŝޡ5r.DbfD;>Pbo FQs0o&!cp1^ytײ\Xy同+¼[%ŕēdKynd cӃ] Cg ZY "|{}1V'3\j:HOL@-tlQ@xZkָio(D⾹cNPA$rmFĚѣV &妋P])eRPOO%,฻,b0^mgC=;uA[P>[+{inobEvUԒrNxnk -ܾkmœc ֢?{U?>ן=p*GC60((/v] heIf8$>7.nlGU1=y*=jγ-Ikylj =u#CKKCn,00>a@krZMl h8Yð\߽4&-1'T- 9_J]ZYxX]0b3 u|o\w=}BK#'pp@<M}R9-=#훋<'Z,ӣ+{69HA!X٢1EQܤe S0 6ہTN*sU[m##[@:Fv@4CGYX'z;:vL?+GOMYM26rI<08"n<\FWp[u!D}ŏ82njV~[PDJGPgӼI}way4@QVw0xQM@I$ nyvIU˯Ziv7R]y;2rlp3תo<; י ˽2~v yOXJbWW%Mx-,# 8WO'vj7vEkjlf(<ΘN=ews AI [O~zWGMzkiv*13H ּu';p9n;˪{{q}%lhCtTkyM\ ,eLXg{VDzjNꓵv0_϶MExSYK+2f2͞9+ 7@'[L6-Ö[QEO ȿi ּ'VG_?jčߩEUQ@Q@O%̎9+Q,}җY㷊,γy~W0۸pǥlxcð3ٽM)!#%QcQ{&yV8ӊ[Gn+$*ʻ㎟R/u85tof-"3rnj5zΘCoKqRrw0P@=iڇ4&u8%eHN: 4kZ4֭-C\ܨvb7= o9k23kؙxS;I$O:LkvqʁIOSL|OI־EIm+ 13sր1{&b}fkyVa%^ 6y^s zCo{6"l偝HA:k"/V2~s>P/YiSCp'㕃 NGa"gFIcӃ:Ҁ3h|Ϙ4m~2@yU_zy;aD dp1ⷵjm7IK;1ya*n@qNȅLQV_=HS=& +tO.0g9pΦM5 odK,0N%˳G\p9=*SOL%1ysuzr(B9&/5ͲK"K3ӎN={/Vլ>*S$t:E6uի ͖2WN{WHKky[$*s2 wZ\wD7[NE$[HqZzNvEX1O18PăQRjz!] Tw@~o3\&+NV׉5  %-絖ݕX0c}\wbX %EЎ8GoJؖ ٻr(Mj .fv>\-"NvprW\v>2gެM̅8 IZ:ψ4aKU˴vǒ>QOnbMfvyxss\ޥg]M*c,&~q8խC:O)"a[yjeS1pOJj܅:[ o!wݿwl{f#{#+YY;6qFJ qcxO_ӣ܆H7Z]Kil,1oUa1ǐPڀ5|-ȎRkt511rway$@aLYٜ|  vT=Z63J<Ѽ`(q,@$w>ƨh^..m'eE##6GPׅ.ߙۘ`ٸ#~nCsU]n)Bc ;ET֫t-.Ioy DhVSAds42F*yʜnjC?}X [6ld,H8P~jAGGky֢f@m'<ⷼ=}.Xۤg;6h$6MYm̰0r I?.wƳ<eդMHyn@5@Q@Q@Q@Q@Q@Q@+W nʢ8<68튖t"ˑ|giqުCpͭ[R$als?ҭ;y" nS@[K;KU/"V[];73n9XWIq3j-$1,hsn:&w=JRG 3| Þ(׼EutF[FV3b Lcjֵ\{-J9Ie@(gv[<8?N[kO^%Xu b3}K36Vc z =K{T }sqB;`4RNryz IlNv gy9<Ѫ+ip^]Hn!x7R=sֽxsP펏iB GoIi6WcIM曬[\=3[ZZ_3b_JVԑ([xQYV[(Ŵ3=? o+*u@ER|w})ܨSEG h@;QEQEQEQEQEQEQEQEOjגDnQA=3S  st@PIz~5%qe<Jay#eYT$c?axNĽ_Z1|܎(>.,ndm4HZ°g]9GYȎ.I,$jc:oEnt荰AHq'l+ӮgynɑcKf8 ^u mJ9K \B䌂?bxcVc[p}ہ9cڴ|5]˥$ #,` `oby94TIo anPqRQր:EPEPEPEPEPEPEPEPEPEP'y??ʲ<'"3ZǼY~4_~QVfQEQEr^/5>FNBM̆[t?P\ww2 T3ϥiG5&x;y@誖::yJb-X9'{R #.)a Oj {Zx pLn((((((((((Sڵ -#FXя%ն8BwOAP}R_07늖`t*w)\cǦqh:r=ݭn7̜$}pq Nx Ȓ= a߀kWE@b6,rI `c սBEaQ}),QӜ~(ZD4Z|Wz]˸3OQ[3Jl9aэ"MƓ>,:LcN_eX`*߇t;{\wT  g k|f6m$R=:Z|YnY[nᕁ"ΏaZ2[kfH@pN>IX&%Sqǥc]VT[X-k tYI$WzΨ\eJ{c8@[MBi<mN9&lnInl8V%bBGj.m-#-7s+`<]qs|1oM92X-|݌kj ( ( ( ( ( ( ( (?{U?>ן=p*GC60((+RTf])/ g()R1 k|#^Yvo4/0ZM)jo; kG%1‘H c~Ro_h7wsǩV9b&b3 wkmoliJr sUPG Ny >\--&v~Apsں ĚEmwr/@Wχ I,yw(lP[EP.ufQ| `#5~ ( ( ( ( ( ( ( ()Eo*jkU@ 󑜆W[('-TS˒saF&rJbbfW m5[BMB Do8fg U >\Ժ+s&|)a'WS.hXQ/:n#\2r2': O R+3}|jBSҮvD#db @kWj\hK%e,,j,ٴis–'{l` 騮HxSN[M.%X.]U( H w'/u#fy. ~^ V 4 *'0b[ |Ã׾*ι +Zgڭchp|\NJ mV76Vq[\ϊ5]+SKSwy'-F3:R˯k0[.hŸj>XPf׶1@%jM[K$UaWz/ȅր:j*MܠdB'zPEy׉Cv ;daX }:cp8GqE@;F3ִnWW_[]Gh<ϴ -݃ #i/M Cy~l~mJ\LȘ`!sÚ  Y&PDne y*ɣjsmLY1Fb@یJҰ4Ui)nZųsNJú-ZE \s~&3U5XPH DO/=Fr*$5H,#[R``🇠D:Md'<+.n-p^^'ҵ[zZYKo P א3{ ]6mlXaRHE3V( ( ( ( ( ( ( ( ( K;h%HUn%UY$X]Ert"@ 㚫Mr?2$_y2?.1U@\>~װˤi|B@$ *xcGLe{yMR+I߳Gu?Uہ[G讂s v …('ִo-Vŝ$\՚(1<5D f-Uzm k8m%7yhː98բ1e(HdvĥxAp=9&*w[Uy բ3z4R|D"!u :( Z5ޠ/%.X۽,mٻ1i3jWO}Jzɭ*(>@,eӭkhttqP kJ>t1mKzZsx{G}5tkDm .TQZ4P]džt;H-'->TE~U^? K 7pCƗo,v%uќh 1PMeM;zU( Ƈ%Ϧ/"Hw|/V°C/`+zբ)d:s]#E7mQEfaEPEP&yYhZ:6yQ~m# ?k"m4owbҘ.S_Nk֛E{Fv 0P }EK pӥ?enϥd7ߴY,׷vhu,JJRZ]AM4W6໸Pz{U<#IE6UBȥwuJ[ hkjbBȣ/M--*[,^eWw 9RSI<j(gޢ|5>d6 *H8#`ҭ.P9nP:v ÷j&iݞf|v29FS}G+4&YT.{u?F-P$bJl;shen' 6! @\u#Ҁ!ӵR jQX3z``;W6*F뉢)٢qST5=;TP[m6eP)cmEw71kjeskcqMy5V\nbqgM5ۋ&6-qp|Ò8;LҟKUԋ&/иq(9RܜI}/M}m.fpȒ ln8+ҴWW_f7fGdu7qc5]G]}&+YѝK@^\`XZq0"rMu J 79iPiwV kxkғL`7 cºK fh bDVQ A뜜jM [ttK0$eq)#?'x7iyunmq,|Yn8VOu?/QI'/o\Z:o<; z˾O/u4tA&w]>Ce-Qaò.r?n {{ymݣ|wRAlzsgnu䗁b m9^ iŶyx&L3ݲG鞕_ OMC$ :XLڀ-QEQEQEQEQEQEQEAu۹~:1紙Gx w5ty4k㉦wQq$H,icEa3Qi5+ӴۛRoHTq 8-3ú*$vad2p>=T:iv:fm"owoV:ΟjQl;۳Nx$稨Ǻ|7 m1{8W^7bU9 ZM{$go7 ]g\G~<Õ\ w8z6ųGM4:M -۸o Zx/O[ qȭ nQ\N ;;,AK$y6ƶjvW24D2*d`IEriIAs8n$&V+ kcO# Hm>bH'oԏ+2;tK[iݷ8FGM8nxOFKY5 Fؒ3Ԓ;UѼ=[s}6 Y"h H<'דY\\|@QKJ%f H71׌=_z\=#Ŭr)@U^4{u8uB]ȱAp3O--u+VF%Zmќ{W=6kM?̲֣;6IiN>ޏ[TuK[U Ap0T|^G|//7\7 3$w)ag𸽚X ꅺn3$bY]-KeӀsUtW[bi/1qpq;[@Z>ZMkܹr)-ug=@Me]>X-uG\1çZҮk6jiK=S xR\izF}gF㞛(𝅽?h >$$ݹOPwsFiH̟w,{¡ཷnf:gX9x Gn8tt?,Ew;O0Y>;o6Vy,È9+fO{-%h $x8z}wy1,yOEQEQEQEQEQEQEQEWKE[4eE_-&5pR W<3 xRЃU89ugs_­]Yg"9 77PC-b9/Y>/ZũP5o-A2ztL3Ctk4-̙(xpyZK,qEUPd*"o#9'ߊnJug'O𞟥Kk:D>Mk)+v"67n8bGcOj3ek{@2&F00]b.u%ݴC*e]d ƀ/lfͯ5.3O|`b𽖠-5~RY{#LE +鷱,o5`B s)pOZqa=Z$(1K^䞴O¾]^7e#P(LB/ӵ[FX pO0[5͢_^^6B$˂=Ph͎5 ˋYY$I$# mya@W^%4니'k1mbWw}zTW3l4}FXndžV^`O` EooZK΍1p( uK(,:(["3+˝4k@wRĬ$e F:s]pٷ*rz?*ݷ#y^VE{PQEQF,DI L2;PQEQEQEQEQEWK{m<ĊcFDmަbvRUIOaZ_KrnfeD,Fp1o$BFzއq@!-5$`#nOĒ X]ff+9/;<Ȅ>F99G_j]+0鶳6yum:: N&;̙ { Vޣ&˘';;JA犡x#GԴhQ/*9b9lOsmdC,us e3P/;cmG PHp0|[ATqҩ^%TYፀIT7zb+j펩j`wk Qy.eۂXǧtuN}:JFm c5r9};ZΡnGmɧ@bps_n `{o(0d G æ;O Zj"nn]xه?+y{]/vU؝.n'Gr+ :I-߭!YPǪZjv6^i-ݼpBPA=+qy"-H +~WvWOv<7_8<@iZۮΜb@P8ڤc֛1aq#O!XQV,4Uo.%Iu!fv`c8D[+h&`\``1A9d/5Kk-/%9@T G²Znpb?{&Aib[JEUB68_ғ]Ҵo{˔i+4&S&$wl'c5eNFZcGHۈUfg[!i9(H(o.-}Q9aypRPT峞*చSM`'hG BR,!^"((((((((Er[tPlaN>hO)BۑAp\zč_+,>VğbC3mf  Mv 31 5ͽ%VJ#T^sX>:h17[u6]ˉE͜k=2u$Œ#)湠[;˹dXYXcQ/NJ?K{]SQ6`v썢ꯦ7J?/p:kkdMف۷PY|5΋k] =8R>pi_i+\`\H=kRkF-Mf& IȪ[<0"~R1nAH(5MP ^0yڠ4> ɹX]U[x2kvovcj"_ߌ۝&+[v[dKX2nXOvTP5nKk[o"+y#E7+ǵjh63iiI #0(((((((ǼY~yO ȿi ?/Sj(3 ( (35 R=)P\<"tI!FlZ~1 ۀNz]2K{+'tޤ0*V]Z[]Kڽk\ű\#h>kPŽݜ^Z̑VlmA=}UoJҥ+-eVF>?uAZifD,8.xǥK{VbImV̶.@Oxh^DӮ>yACv o}i> 6ڥLiX<(ύ1Tn DSsgMm{hy$nظW*@v'n3lޗw%2]G u_HuJJ$edp2rzOڵ;xn}ANZ੢ux} ))@NI*Oәn.\̓mS[o9^dbt-Q^zM$v:}Ծ@7C |dg8?GȠ5ҋ8fH&\cQs>etmk42yd [;$XZB@$\ IU :4KM>[LnBIzѠ t 3EQEQEQEQEQEQEQEQEO ȿi ּ'VG_?jčߩEUQ@Q@w{-̈́-͜TLX+ReyXsxK],<ȥk{g2mp:k ,`{1w3B`2b5O-H5 6z|3r*+!ok " D9x 2lѠgGF<)SזM{- \&!9[kPsR_M-3\X.Q!]$c#;OJx7[na{H1,"dl;H5tF z{H#Jdہ}r+7SCOOϝ9b<6q؃>vS.4岆X¬U2mI cӵTE!b$0brFnǾ=mCOlV!;} Uak${4=aWA8L5σk.Y5͢p`c'XW_gݖm:k&;#+9SxQu{o(M,̖O$l|=@@ ^tC$^B%(Xq<( P[Ȍ+F㨭? XHWY#YPsӚhti^ Gؠm<I_@9&4w%;=4PEPEPEPEPEPEPEPEP8u[QƊ"5CA0s\̖&EH\qI @L9z~5<I3$jY3*]C ͧ%VS rM]ÚG}+åG4f ue~gW%o}GKw3j^LkK,Q~1ۀ[PU6JeJ qZNm-b Z(+@ wzƅy1I>J,YB@f^%[H 'Sf_NhkRAb].-/Lerp:r(((((OdxOEOfy??ʲ<'"3P$hM(((bo핉T=zv<mh*9*ed޴5 -.Џ=[ɰ0de88#9zdM=GignJɰ ^ IJկ)q榶+24RTrxzgpOqq6l>;p5_^,,XmtY$RMm;vLY[]&GAoA $nq~kg%X]qW1{fK;+bIJ:g-:?-&s*pNޟPFtǩ}V~0ݜ=sKkM kwEu o $7 qd+K+)ln.pZvqqWE~#} )IbyݸP>s7V0g/'O3lc‡+PEPEPEPEPEPEPEP :GuQ#73S.#RV8ԳaUٗZ61"(H튛PIdncL #ր2t}_z,hOM$ị#iZ=՜VtF}XtOxOn42͐%ϜZMUž9=1Q~l4b4HoaE3 mx9FG|;-F}&K{ymYpK֢٤21"RGO&{?__Z\Xs@̤fTQ;{]V)m8; z@/ ^iIEmgIqگilZ>mBŒ6 }W( W6̓0,a@M6Boޛm<ˌHF:sWQ@m2txg|#zO,t!r(>VEC)˾=I>inl_vq$٢+izlFX'qH('׊EQEQEQEQEQEO ȿi ּ'VG_?jčߩEUQ@Q@όu F76%:Ǘ]=?*BtIyg6+EQEj5l7[)5/$${<*'|Uu-pqP{Fho>Wmlx1{E *z}CDJ@F ~vWz\H hHPAEQEQEQEQEQEQEQEQEQETw24VȿyPExf@fYILƧcXqjS^FE*RX  (.%5d9إAɢܢ(>s[[(HM`r^!Եuo}41إ,Il܆ߑEPדlLA-2r=֝P7}JP'q@{z/ څpJɷllmOsE+|=}un%"QZRԴ']E'&YByQ@6odki³mo,p?x\jYʙ=PQ@Q@Q@Q@Q@ =p*ۢ[(F+7E7lEQEQEfotoxx-15.11.1/data/images/custom-kernel.jpg0000664000175000017500000004227412616075370017341 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:tonemap|resize| 4http://ns.adobe.com/xap/1.0/ 299 291 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?^#%UU9$uK@*GL<kyBvgҸed0x?'&0G>)pfB:Pq^uUYg.һI/$ê=kJ5}'6$Vm68k. #\*Лۮ\7M,J)`yUKR0[m6uervns[,1@? l<M^Eq3%Xz Yxb运=*<{cn4kقI+gCvǵ\ú4xo^Y['ʹ0";!F运'K=J+km]K ]$SJ}jM;S5@tJKi4f&5BwV=Ox_QEy]ǎQ{Dż9p=? U}C#MFYǦ}=Hi%GU oz运x_W!MzZ{Y 9{y7uSS5ye!"V !S*E ǢBx_W<F"v/5+e6N:\ۤe{2Nq\\|=!I$ƌTQ _j0kIg5͝Wћ`ʬsY}o[`լ":sOpk7^E6 ^Dz$讅Xd b/+ZG?ʵ|z.%w_Eb/*GE1}y运^gg |_Q/Byy\,]!G {{pw_Eb/*GEŋ{wWU8 r YQqؤ.#W.饴kuswDRf)VU(FͨjPws>ҭtKf2H~Fj́Ӎnջk⟕f)Uƚv3GRNRճ, ##$% UAϲ-Oʪ̛%*~v\uG_Zj}xGm~Tr>Y,p~5^o sipigx\_rCg?ve)Q[⟕.qOM 4@X,/-$6N}Y<7oVm~Te)QfFWGZe)Q[⟕.V2<-Oʏas+Σέ_~o+q3MX}ۤm8152Ѿĝ۹t<`;b*io>{[˰I2?j]@lfVD 8#@iLYn-X:o3ހ=ڸ~H@ٕ!xzS-5D{h-Ǚ?RQ@E[lU+$N2F*k =Bm-`\b9WQEQFҧbL%U^: d&IIX4pdĆ1ZUGW @0tۈQyhʀ>,ZNcj i$N|!p}Ei=OHu Cl(͔5OzG "(7?ֿƨhY!wk#=\:wsKO_Q6'ƓhZ!:s .iikWh ?3hY!-?Goom~UoC?S$Rb(uj@:}k%h_̈6sy79mHl%PBK H@P3UKI@[Ayw s X{FinZpq`(#gj2\X>"O,nt{T1:dwiudHVeCqU(Z> çYy@{Oy`M34PmK!2vo*> ɦb;0,i26~lc9^\WI n2wdښw[գWO/ c *=~T ~TUA1)VQ4 0q**%eaIF;FkeۄkxSFJwv?t]].KQe2-DTb FbsP)3,lg!2`RB >oy/ndi4mJ#2Rv٫*=Jʲ~%Uܒr?Ju^ 9VvE8ՕKbL֢{8 ckPIUoj1hj]C,(1a jc0[D^` x?˥_vO 弉c)j'|"iW7sMskul@F #հkj?Jb.ڍ l2d^pea~i166MQys4]P 㹬h4]R(smGlEm,eK:I 4}n!D'YPYF7<{PeӞ;mY U v! 6+0SduL + "-S澳Ѵ>\@"XMGx?S?p? UףK "{ /(XYzC4m5kSpKxl?iC ثksVT .CҸ2M-ܱNq;T"MUUo*I?,0?LG}5c28ʦ=@7 .-056wd{sv4߼`5xHwЖԱM4QEQEQEQEPEPEPE%/ҌI@ KJ(jW+r.,NJlx$dA1K@n@ ?Ef>WaZYgyA_6DԮch^ {x߇a)K@ tF~]YŽ  1= igτRA_ifo(-W&.j,qIJh%PQEQKE%RPE-%RPE.(U 佒OIZ GZ7sr @kgxO=kEԥ& h[rB`lı"_/[:o&5Kķ[ԵXdu,VQ+BjH?VZC2/Fӭa0+M!]/[[4yV#޽]jKhbԬ币L@bX#Jk?/[:o&/[:o&h N&9c=p@5`ydSUݤ<_TG6Vk!nKlܺ6U&['EٿAA`xĚ֯%="8YQ C_Prhc玟[玟[݈ȣb׭A3gAtwher!9ȴu=Tv5+gi-Āƹ:8Tm[K8 %;ZƝV"/^ܭm*D$p"hi|/\_,"ehA`FC2q[84bo&PH`9#}H#dY%Nh4R F^ΏCRVvsܯ%te@G=oZt8f{qIJ[Gܤ~zUCk%̷F% %(bL~hzK\4h]k\4u[h m箵~.?cK> V4,@NG `jyVx qTMnLVCy矮iFo!( .p3B|QɴwlQDz Ny+N!v }~Au-?ȟTeE;/Bh]k\4u[h\ֿG6qLmZn,.&sk+=0pp V!hQ,@?U4O [Wi:]\=WG\ܜ*Ԉ\,EYgֿLGUh]k\4h]k\4atd֫GYKu]Hd ~Cj~bk>/&KPFWe© Hȩ5IXO-DesکiZ \4Ўf8)v+k=uqk=uqT:㡘VqԬɵ`;p?KekG!OƐ>?A* i1j$$R,KqЌ;>ѷwRӀGReE3/B@ZhE,I,KSsDž!=8j Nmi= |UB.cn>AQS[jQ8bHQp;Chih@钀71 ۟WZV2Cu2váρ߉ɨt_j^DQ]B LD\IO8cx"|5v&1EY8rn3d1Ž)밞]ѸӱF&U#cϰ21?YF4[1;p*L?˴sس!Pj)^x\DK8VT[TڹwS֡HZY8==QҪPm $bQ@ tRkp-c[ꇱZZ3IaΧj̒9wXs@~fk6Iqn2RMkG[h2ƍCZ4,l42E*xN 7t۫/TK뷺xѷ󀫞(7qeԴyp7y>'8VֆnO, 1E!Pw\5ݥ@q1GN1۵N+.+u]NfHq #P}PV4oc>^Fz9ѿo|?ڵgFÅ_}LUGto[49cTʑ>$;c'P}6C+pA5WFwvH {Y[nO|nK4$NHnS0C. AG+hs`}Pv6Ddj'|dtz$k$v2 gZҋ@;?MWwFuG#7I-OvԄHS֧eYxբ,JAڵwfB,\9FsRPvMNQ%f HĪNOox~XZYʪplCb.}Pv>ը;?MA@Gto[4%nhekxq1f#*eT(fjZ|F$.> s$92H)ql겄(UUsCsgg6 ip\ѫŰ3ޖ]ʑkf~Ev 昋j?畟o&j7Pi?ѿo|j 8K1o[By#W. L OQi(I}jdy v,B O\f6BXA?E˟krI/$vHU.2=.dN$@_j?睟o&j7Uд9Ya6YpAw#7b, @;?MFOsolHHI=xn#7V/S͎gH,CPaU6Wm$23$߂)v@,xm5Ǚa|?ـ;f?Jm '.$˺+c#几8hj1N^ѤCYQ,Ni^jӎVjBO՘H{Yơ}w:-ڝB$dQːFN:R6JIo0+`7SZf__Gv:jɟ[8V2IQvpGҴG&{@5du3*KRW[}Py{ـ8MYƝm+"#A& :jzR_=znYTXpls֮Yٺ(`^E[IrnH换H iFWI.t#U2RGe`!H3gRgjg'm|E1D޳׽^aZ\Ilv!93B-f__GnAeq5E oP}R3֗Le;A<ا<9{AOUր\;UEb@QP Y2jPO;R@͊>)+ֳxiXTeqwbnAeu?u/ /[w^ib،eOZ8)\jiz\Eq,H`7`?nb5 K) SE!tH J&`$*pHr?55fBn>,eT3* !A2N@ÚR+l~Ӽ#ݎ7c>O-?_)[!Tu[h%Pf_)[!T@Gjy/C劰@U\M)hܟ@fIo*WiR#:+[i&Gؤ2ƧYk6 FVR>Ԛnmke$O$~d2ƹ Q7 r/*mEr[з/Bn_P.b-m?N:vw_Ie9R7@2Ѥ2/ΖZ2O=OQIxwLҮ$ܤ2һ\j$*Vu73HS_yd:2fLGWMBܿ o_Un2ry$`T6$=U+$\f|˝k4e(i8A ^Hu0Q-U$%XwOo`YWDG?A]C Y2VMāxH=<76=*mkI r/*:FXsXl]FC#0á(bxr`p"8n-ev#"f=#)OdV?5Y/o; 7;H܄~5Zhs(Գ ,&Ā$k4n3{Hol͵yMBܿ o_U;V:gI5 6%`+r@r#r/*]"Ic3ʎ4h_E\ "Ƭ Eo5oSp +wP iozzy72>߻mu<-[SyҒ>gI;wgCuOzkmD*4y -5B\: G&S*K!P7G&3Rji+kquy{7M'%=/_}?0%I)c-c#&ԍsG|-"nRJ!#xsHm#N0(i$y.'h柶5m-E6:R1٤?ZC*hNIT뫣} "kZFT$dt{/4sOF"OC *5UoUү?*h6@hR`CjEYxN6(֗\e`o#bc_ڦ?ﶪ uKcM+'2vbT0})ih@ c(]KVQSƵLFf_x W|AyuWvVw,*_yT Ҫ6Kqdsmc$12L0x:{hRH:e{L)6۹A{|,nX&5<(QJpIZW+]b#{j3W;G~05ޒk?O2SEC00淙 lt5+ `3J@ ~GKJ@ !a?وNW<ƀ2X勳1bNI4rgi>%}_o_WhoaoD q{?W_%q{??wghW4[;+C4J`RUYXq;>]G9u,cZ٣>n#2isҽ/6ЧBPջaF}cGh0.,m -N9*SupfrRA靵s4f3f]19ʺvg ǎZCrO3@Qh1h G}j٥F\c$5zDXۀOϰ>€Xϥ[jWC5T,ο*.v2}K jQDYn${|{>Œ}YXd!)!H$wuCm8Ϝnzhϵcf3pzƯ@v@ mޕaF}b5YToFM_f#IY4,'O$4Dw+=zTo<֔O>W'ko4gڀ1` 9}9:ҟ6̎:9̤1"3(ϰ )otIV4}9? W)X摁V븱Ƿ5j3@ZFa##U?fotoxx-15.11.1/data/images/make-waves.jpg0000644000175000017500000014466712616075370016620 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-15.11.1/data/images/colorchart.png0000644000175000017500000000512312616075370016703 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-15.11.1/data/images/denoise.jpg0000664000175000017500000005115312616075370016173 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:tonemap|resize| 4http://ns.adobe.com/xap/1.0/ 316 316 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((  " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ZR8QW8ɨ.5K8d7tTup:}"S[+*έ #\xOKVz<ݒ= |%Y~)CXFB 8<{qq,Lbnӷ?λ7ڽ".^w҅eV Hxz4R\T ;Urk;o5yh%WUX:U=Rhx"eٕ] pn{rdfB>ɨjV=vʌQ(҃iG)hзR4\,hoU7Eyw=vǺ>meK<:gdi>:T6b]$Wn`G`s{A!F?*Qе7Zk2Y^EB >(|]>~q0$ry UO= 7^Wokze֘׊%$nsq*?ڍ(-Ki=2]ymKvH\3[n:v*MAZ;:ًC#(|(A䫌}kk(-J2.aCwyj1Zixe)Kzk )=ZX^#G *Qt3 SiqqЎx?JoX"/wh6Vk-J>oo2Gq׌V{g2\$Ns:~B/?׭i*iCl_1R Agk[+bkt`Ӹ*sӥgj>H["Ϻ InzמV2F֓ V cpl2t#ZR%*kֻ,mDC]H19z|_,e֥~|x6<#5vfe=~bXepʆ1b /DJ%"rhQ:[;.5`3YH"Sα4ql^-{=ׂ |{3$w18Vx$*۰} Aml+rq%̥a#9N=1P%s( wÇ2y0gp%_0#T]Ow;:2x*[IdFQh$~z^[aksH%@dp ?Z_iL@ 6M^;mj(hLlJ얋n=@'? C$- Ǩ]8~Ҏl_;;[+rB +}PYxf4٥Eo4 drHrx29u"q`Pɻ%F; nZTne6SeA#B5 xZjw"`r;>隶! J~-y #$xxoֺ\^Qu 3Ҧu#t7_jwDX}4%MZ wW9%X.Xg^/MC$cql6hSiVJ[ ډY\9R7 s[V+K(eyX J::1E)|U%mx ]pycckBq:y%=?f9zH)diooIkm9cqZ˟-Rvp`2m֧5)Xv, p+;MG"]m0L$u113Ѳ~ݚj"XϹ92{vxK{o1VF<@\|Oc.v Sv݃=* u4,q7mKP򧓷wo Z:(UMn >~a8Q#ҼAi\Ɠ#Iǵ'QԩVSGс}Ō-f(']:?9mϟ: q'(RR1Kc}}%V̻vWIR9+`tҤGi:Yܠ!'eP}j-F :[)o˰R~ +&-~IX'l8žFjgengE:Ǹ|30Uz( Wk"^gzKVD pHa}&=9k6:4eVex^=n#hA%ye59?XfQ3+,*ZY#*ȁ PY>iksmp"@]WGi9T ]B(R) /zl M$$qVc+vHtoJE[v*tĒ9>ݖ*6l,T 96$3)eSjKhvE$GbGPQHmV7uLʪO%|\Mh_K O"9,qک 7A0EFR?(LMoXA o0!@5;;C7ֶjq"~`z+lP3t{F6=[u8P;qι_G5)^+I|&V98 vdd9Q}n+ice]m?G6rb;KKwA/ \K%tσρ8ʡ9l#Sֽ'4f֖1)c62B SIl& F3=s6{ @'1#WK(J(<_i=5"@$Mk uj6 jK2p<yN{ Dڎ%֟-v8u]vтLњct j*Jo?1N sIQ֯wZψ.⹞yNǂJF{]h x-R+ktq(PN8p8AFhhzfjdԥ->kKVk}; Óڴ.䷚ܧ`FR?hńiqB"F7(#5>+?B0h_3&A_ʩ0ǽi#o߻xZo5Xc}ghGQJ|ejQ^}$`"9SZogL- Ʌ wK?soK{nVۻ:іF oDgR?})ogL-y_ Wkkdia@"w9݌`z1PBd}?ahP6U#`~*h%B5vis3O0)vsddc.z:tf3E{nlܜqVe}(5re*En9U= ecisVw.%=J ma@sKi-g7κ[{gR|Msk 25ܥI$tFGLQ\|j>%ӭRso).v9xK'gIhc]s#AHw;QY'!K%*0pÌRU{}w x2ĮOBuT%;m<+n$ (ѶԘTc=+5K/,ykkCrİbpn[ҼK3Aen^Gky1S`NzlW;-0n>vfrd|}Iom>>-+,M+7;  P~MQЯFt'}⍣bHf8λ, +oZ;H-%+zdw]ȽR6j/mƫJ.o5ȍD;9jmw1/?bԿͷz$G,6卷*ȼR6j/mƩ",mb}H^#tm\,yǽghn(<*4F}cf#'' siW hfqXڈ_$팞gNSIETWv[IouktaR@:]ZG#,䃵X2f+)a1 4 d23F sV-gCiQ[Gn0`DyJEswܰJ@A5Cmt;f `H=U@83Vנ5+O)X ʲ.6Ǎ-,Wfs.%/o r%b26ɣmkUmxYe;VL X΄s*aX'^8ɥ mEmkA`ొeTYB' KuqҬ"[53\DoU%A/ +_GevN z֜RG4),.ee9xs·s5*5#p0$EYIV3=Œ:_:LFhۋ6Wz<,'zQZ;V-$ӲM2<-/Q_Qhfnไ2 <'֒ZIJGNe_`p2?Rm4+a>/S_T \npY}9mm>ePV̬vCDRt23zo2.3XUa= ;~?'7 pf8]$Ϳ:;dxHdR2HzP-,_ųi&[Gf"+bvzt]HıHduǨ m|[=:YҚaKo$|$1]hUQFax{@V^GM^Z+iЍ]0 (.ǩ5s}0{k5 !*g=[J6EeghnFu4i(GY#I#`H4Pњ?Z3FhvZLњ:@ EPKIK@hgJN&hAoyiq_1K' m0yV1@ @F(h+s9FXr:qVϟI,,HV6 ȧ 폩z,W9Fug_3y\U'#g.._*9Subi]T?iܹcwM\?R@3͇?:3ʭ<*_*G4srT4I??:3ʧq5m` $KH|9_~l?#$e%UaaUhDA!o NmݜgJXKy.?F$PbX ((-V'{&yNdtVi )E%(4thSZS<9:Kpc{yH  qcXy~+0!*p(Blڻsc35Mv}Nk[{놂'x^Ws] GV>M5 ъ)zk\E}48*UA9 PŠ(aX>=7WUp7Jޠ <ϬG8Lx{Y1#Si:Cmw5ڪQKg9<++VMWMF)Io,)YNiK~@cI5 ~O{EB{+FOvT1)T[ yi] kt;7Mq#Nj PQ1W+X?(-ul.Q{ !=1޼ݕЍagdî? ]SI4ߌ?JKc'**Cn߁=VqTF(nTooL|Sz{U?-Zq".Fsҁ:ԶY[˵lW=J&oy@;W<`3~GGޙ?#o 7Cʖ&kYţ"\aH2#5FZӚU[IBޟJo€b:gPB392wyēPPio 2#ОOHaJ))hKH]㼶s pIއxƚn"AW9?a z!61IV#apO(ǓEe5?i>SJ+7su.f-,;ܔM5{qj$">p ٠/sW/Ky V1@|Sx^n>*X²m*G :-Iq<(X=O{_h66 ?[?Fp#LըYyR^oM$i-#4; df; dfϫInC_%>b?SPh:Ƨ{[ռqL dp{NAR)[ \4?|:^?dfQ P_i23Gi23N4Zq}F'P{Y/7QXizw(%ȕsI.uzvq?y ^'5t'm;KYndI#`('3<3Ml1:<-ٯ|Dz(?qq|U?.7YFe>~sJe9G\ VӥմSIE 844+1ƯRRRhݟZai|1!B$eH4okz3}nHʨx딦'ۻvz菱TV[4Oivv BdwڠJ=RWU9<n zon66*"~Sk-]/r tRM\S6BU[VD-+nTPӡ}C֩pTo|Q@Rs\ch4`Q*krR嶐J vB;OkRPr1XwaF]*V QYp+fM+Ð֗+q$ܐ@Ϝ*%Ds#!k(D/95Q@އ8tF+sfˊY)B;FX&92a+a\>C69o Af#k !c(!X| 4m0}4Yx@b':[vj%GT~$n5)c"mKB$GnA#jsSGO[mslG-hmPO\TyeJ0}mRE]A\ѬB2?R5;Iw~A56C8!㶉X 'V<1MK8'R{M,5;mnK3X9ڴpk65泭]%˺hGfeyN:LM-Iuw#R6 6rAIZ[UỂk4c#lלc!bd &`,Yd pү5=Akۛxй"d(N1gҨǪɢ>*4\玽խ֮[ YΨJ[MG;'"/˟GfԤ'M1Rp!z `>C"MSFsN1FE-C+&b]`YH(⦪jKTW,1ҨZxH+;{֓[} pv=΁[K!srYJ  ~47cF=4ˍCEPZ[2ɼ(qJ˵Ԛ{żP"ex,gvt`q&) zvToegCoYJA_ʐ~g>vR}|ت"&cTyрf# Y/4J+K"{Cd3€6.lŬzT?o~Kt?'m-g%xZDdGu [I[x>FX9:fGx Pk!F u}`Lj\y[:^ ERŊ=O ] MbUʥHaJ))h)4a c#t5%FqEYJ)(ۨ6\҃қE8f\QE⒊vi R=((&(84Q@ϵU䁛cFJ꒎e><tj@'Oaڲ*!urcas<4fY??dUB4\,A>,HK)VO_/.3Eg _]#7,:u9VEQ1?ILp ?fA($tz4OTN#$.q=\gMO H̊۔;ZѼR.<9o^3yL?ѭ݂9acj*LCukfn?v]wCZ|9m,Ra ,kbQ̬pPp@=jݟY+6dzv7s:sMmnCQ#)|Eeܶ+s U}{*_q'8v=Q e"P+1|({%{jm?%}L"۱w@.3dsR?}ɸ1[h-N*Q}$Sۛo ȇn~PAʎQTT9`9*v0林 ltz^mh"ךЬ@]'mnb KgsVHf4+-Kh捊Hj'¦nw{=L'˻$c}fnK;" dž-m Z?N#@Y!ƏB4i_~#7 Z?G'F>Ch7?|Ge}!<9Ako_ѦױO-cg礟 <=$P(I?1yGzILNU($Wsē"(, #=jqP/(rZזkzmg1&"/ATtT7fMoh*i?MjKz_\teAk/7\xUk-ՖXTe cGnu+kIetw&b-|gA'(*钃Ke 4/jvcr؛و؜9]7d??%%AgwJ[Ix!K!: GNUS2`qaIMwuqN`J(.X;Sm?!航M$UIHbeAk7K'(p:W<M'峆ԟ6e_*>0c-oWNٯcHQFȹezv\]lurZӆ(?+֡&-ٹŒY6fDNXpxx{/ͬT.e[ȡ`񌜌N¹8b*r94?5 D~c23ǭ$> "]WC4$:\b?n-EFb\.g[|+gMϐp'xvj߄u cP5 /uHKZ4 O%^yiBAnҬN0ʑGZM\޼aݕAl8x{H54C'X1r_P?IW@o⾴'H,ceW§Į;p+Boq7M؞?V~}۱WoNs@zW\t̼vǰc#@Zamޣ ]%$ Ifr9ǽlxOWQ ~Rʓ%.OUÓ1Jv5|X)-I k*;x$ujfzmoSD[QC6)7mFY˗?n?-EB\kMt֭iOoiĬ'ffT+~|[JC:Yl.aGPiI :S|4$Aȁ3X $ ׳|y_o´t׼;hCUTiR'cs8ޝ?NKjų\P"BHr}wI]P4(r4?‹]jS[ȲE%wČ%Z$$K4>Z5yBEUQ($rRTIWP2YEwlv UBєR|C=xpQ9:]?}'v@D-G2(!S皚 ?LKh`>\cI]R#KR ?€O+ۈI: Ps߭#O0TB *[#>0:M(dG*u0d %Ly/q-mI230OM.(a%[ &:|{ kT# f?Sc? t |Zf OFHF` "Yedc~'yY[0sZ7@ x'c qei!c8RMl6nܹrOlάc? t ?:Esiw1I̒Lnd z֣mnBlW9V:O??€iMF}UXe܎ù5f3 L0cP@oacllaGfotoxx-15.11.1/data/images/keystone.jpg0000644000175000017500000005721712616075370016413 0ustar micomicoJFIFVExifMM*>F(LPhotoshop 3.08BIM0$resize trim resize trim resize trim http://ns.adobe.com/xap/1.0/ 8 282 407 1 2 2 C   (1#%(:3=<9387@H\N@DWE78PmQW_bghg>Mqypdx\egcC//cB8Bcccccccccccccccccccccccccccccccccccccccccccccccccc" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?:f`z3Ht A'MsVG9>SHBϵ4SjXmd,*u|Aہt#Q@_*a #ї1K4?k~'#?-ث"~$A _d?Z_G߱G#?-ثKy*Q$dgp@G߱G#Gmح!hf?߱N1 _Z4Po@OG@OZ4Pw@OI6/V6Pt-'´igai? KZOmhP@_I6/V! -+DUaimB1$1O,Rs #MRx\HMͦZT?aпg~+B>Z:-Aкf:4x\,āGd4qa;i?>â.QlT#ic-#Df?#a\/?4}D]&%u eբXnzy!@_M'tO8h!Ϙ>V#mJ7=Ӱš.I-?Mv?58S1\%N(`7z'-?>ǡ2/UZ'VƢXG1f^)`/Z߅ >Ǡ6Uլ# s7:N.W&@?}@}J{kj} h>WF V ش0/A / kJ#"(Ҥ|'ܯZ0XYߥ Qha•LFB"d8LJ͜@{? eh1^hWO_ s˷ln& A403@bi_ +UE%QOJG?}Gm}73\u'#ºƞOkljK-Ũq[pY$YϼcS-.y_1+jYcT ʋ@[O~P5VqXie!vODCxylg]=t֍.SIlH>*¥hc= >XSzLYPu#pTPRY{2ݙ-'"Eeȩ,C{\.z_ΛvQE?Y cYbsW_\A=ժR4E9ʟ?*U': GG4* 87`~ PXrGmo4ʟ>̿ኛ˴SͼE?!vT*H3L.[4Y5W8W>ՐAQE%di6j_٨׼_րf=Ne>?Gu~? <ǹsPMߘV@>B7AABZ̿|lzIm:o去ֈz^##摟OF82}+6g,ZKfc+Q`_ƃc8,v$gՉ4d>K091[ 7Ep@p[j{KsSqvT{,'dH;H)3V"j?0P3XG-Xy0ݐx=)Ef/g!d`zuVf>YMY7Il/?ZE_)@EP|S pJU263֪\Ek2:/PYv}^r1đ>Q>#P_??ZBUf]⥼Ѯmʘ!zM W&5WRn9#<ZVe ;}[%Rʹm9=)7p:=rI;9?o[s{ʵx+.)kOT QHqQOu-EqLD,sIJm ]ɥ:92ϭTI,jilKmm$iM -b2( $VfFj*`aCdXVZmKdVXb$G[_hZ,c<@c䧚PeS)f9چl \GZŰuͳa?\ОRԙmr!S~Gz {VZE[Br2Њ/.h í0y,`| ?Ϸ򬣣!azvD#;\R-n aŴ*mu& ʑ]=Z%W$ӓ{#ڴeqZђ9xY) Gz++ &Qtl(#*+bq*#}AO$S,ayi+ YF}@Ú?*<:"^ AiwB=`A"H5i,h(b٨׼?ֵ#MRxj]jWBmңT~7?$&q&`5(-D?| SF?;.Ka1}?V4dҠojJrD#o=7m8vv (i\vMVdC!Nqm@0)yDZJ$e]U p!ch9"twڱjծqk *s? W9JW% SQI=@ӛiesYSsJIgd!WZzf$2$pHjeuy;n›*ɦ^ Vf~Q֕rB0FO5΋q;hFYu |qsG:;4ubݘmֶ#d'Js6ͥj +#!éGp;zcj3,K}Ejy-ns֐vWv{5)+9֟ENBN;5eA)D>Ncncp3ki|ٚSDuaS*u?i即eGg}}+YX? HzgI<`|ejhzMMn7jopE-;[c9c]M mnSkWT u'd%d@o23r?|jЎWݲuud쒵g{-h<KiXEmX+6mi 'vف֔CH=_C&(VF#f^Z׬;FKChj^?FTԷҙyҤChBg ~.sdxsYr 15b,i\oO[z7^;X5 ι|%tKG9Y=ԁIgp&@ =K\.abt Tڵ0YO1\(Ms[3}Gb[8'$lW ьw2Agk_adkˊM.;MYLneCҾjhq(/ppڣd{N28zT[K')i1v&~2|2Q!o2PAp66)"Awncl?LӔi=<~FnP5auO#Ulnuݪc>O&DRHa֫^oMYc14CeLW s&MwmO8kbw;v4I\Fqַ[RKzm!GY76ZJc6Aw6..$af:^ sv=+j" 4gifueNVv{jz4.{d%I_ǥ>S\&O?,]/BN3k0;ȣӌW;⻲ZI$[wns\)o;'(HtNqEtFed%P8oȊ@5᛭Bܫ?6uxL"o|ZЂ6\Z?{piƣ=_d܂YF}JL\⣓+#)$|>H=jCd2_~F|!rI;TkO mexdVW ]%e <aʫl%fĊAWm#֪ LdZCDRP1ZӿmԿ/Z۩KMJ}Ŵ]?Nʒ- (Y:u6HUYQb88DF8˝ 66R$~Wbmto wzl%9f\rkl]prf=8淴m%Uk{X]rY@K;t+ۢY\F6HgJ<(sZL2)隫} P)'K^5֗9̉?\ѷNQ:A *~FbI $'4 R1eWGPG?`yY{`Y[10Uk[{Lپ2I)R=:a#C{}jk0N@ kQ[[UŸsOuE"@=5ZT.²<;lifr^kvÌps+)^B,$Ei$-};/i- ln5%p6fApzjuy AĚU-&CqKQWKB ւE޴XnmX$Jb'oEgxz6MM?᫈\}3QX[ɶEDOӯd@*U:k3ķ45Tz֯Cq+.͉Us8U4I+a`lsT:X"t>L5)n>pFU n}_͂ Փ,N,JJH zEyԦ]S}K @Ɯ8W?K$n>Xu4gH*ۡc}9)V#5({Kp&@* t33`{T$p6cXM8N%R=Tך@.b?z\*CMIK֊%di6_Z۩׼?րf?Ey\&T]bCFCG‘U\P^kNȰ|̠|幎`m吣=Eea+W:UGCаɮO6W=OjV[hu j|VC2i[Vt.VdM>T!p3:VƍlJ 7Eف$ ̋ϡVs \ 3Y!,>݈f hz[Lm7PʺW'V"~6,sAW-EKa!.ӹP.)!!u{]Ӆ i *՝gm%ƙ,Zڎ2SzZIܧzdG^AQPK U}қko-S8=vlo"1U^h : ]LP-֩h׬^fF?U=og, ߊ j3}mڥǔҬcٵ]:",9%@y5t qQ& rOJu_!gr/+nX&~Kjna@yU =-)#U@*QWP4ME-%v?5= q̫ЅPܶi[Ɗ|cuB~AU$](+fy#cQryz\E\RzVnHre@G;l#aw46v@ˉuGz~뮁+y97=? fo~Td01C9E qБO1+NSZج};FOCi5o:O`?W?UXG 'Ғ1%f5vc "8RC@qޖ"Ja?^>n`WW48J.팓etTЍ*E z/^]Kp=PI(R+%8ls3I\iV^ zNy4o%MR9xIUtkn#G [R 8MZ \`; QI55`S?<ZBPp."ku'پ[Z{\J3sqߠxO#I]Pm`{015"8jGó6XuQwsWV,lyp?{PXF&oq'}?yۧ9s'lP F0!@>X=X ܅1 'b8"BXW$YYmjv̶l69ZUi9~UGx+V$^%U4Q~S׊?mG9%,7$'ҩjv+S@'ӭlƖwo^(4hi$P O8FFI,R~P >U 6.@+FKulS&Vg^N{Jy#n^Z}0yT_'̴GA?$K8k}JB; N~O3Ta2>jb:D5{/B3(DX戔oODʒvc:VyAT;PNj`ܻb8\n[*ky~*\@?Y< oc#'جa/!sVpHaM3K ެ_ZkH#f䧙aGOКP??*L -5%8jM2v0w`9Db@>3q?h >ig8 bobZQ|fM\|#Jߌ:ɻ#=QvieT6RSq^dF9zZOzȁcUxU\}3N;Hn٭5O~uJYY6p眷Ƶu\/w'J ۷ O&ɤx^Oj4OER(Uۛx&=zOSLFwK"]w >??\PH>= u9+JojmR?]`sZ z,|IQԭ ?h^H'ҰR\8[d{Ҕzs$mD7XVLmUrrUjƟy qXpƠ --J$JT} ~4m\br.dg&V5̻]i>Q&/tHt;JAQmWacҶ{2O;OVZqAj e0Xv'V4b8$c;k2kXfDw]IGbʼnʴV RmR<}$F(Θ$O{N+i"Ŏv^|y\Ttr ~?\jjE!+ORZ٬m?FOCi!R2s\S.z2?PgWe:뷭D]iڵ%6:Bx͕clëm`7#ҳμܫf9Ƕ*Srj pO$TPnp>wsYQc~l}A V.`H#=jZA V;8i.}jxzS$[3ߚ˼7lc)K:ҧn>y pz >uݒ::]Y@ ?ֲIVƼ#M6x$'B܉-av$D@l<fU9/ JkT=n;uFIJۚifbrMIh9 (4lsԊqut+9\*GthBQ[kR;W:ch )u~yQ['X 6?TsEl)U7#طdxcZJٛ?zZ@2?J[d&oHrOu=qYKl>~~o[Jj2PzJ:g'zSR:H.rQ ;Jԉ[pJۓSDVyv\&3Vrсza_yR* _\t$ʠ`gf>ҢvnWw\Ʒus*3R@MS^iV] 5Pb1wC1 3ʬ0,9$T2}ED & 9krW<)6}=6-0~lI9%:,$8# *&G8H !XX s[;}c#9X~?R'8>&3Qᇮ§Q7^RՍa#n^Z٬ku?lӺ?%S}rj[ i5^4۾*<}=i>42Z :kC{汮)޴mƗ'˿ F3[)FkF#ֲ3ZnT'_@+!*йEz֮]1bU׶k.mnr>Dw L^׬ߨ?zlx:Ԛe\yW#y٦]G- A#T0IE ~y7-}Vd {Aث~cғf%E5y ЎOQHXY`1֗@E{ٱ '?t5r@hȏVWRQ= [š`Wu@ݾ On3679\fO/jmLtV#VWX*K$_%~LF'mWBtF 5?ʲ4;u+#mEFb*hX+ؙ?7Psڙy;4kh5RΘVlpϭswqI ̑LRx=֛-ANO.\k]|=3@**W\ۻPd<>;3$@j-B!e[QRՀ~Αcd.EiiRy3Dy71+Ia?(VtMS.yzr'pzRw3*B*ME˸jgìm l1;E2)^hJGD'֭ ˟@5Z!(7+o yWif2;ox&ޝn29=e;]6"]?#ak/Q{Sm} ~Y Q~Z?ʃDa#v^Z٬ku?s-7V]roNЖ.7=glr3کH ]+ro $Y:LHFwcI&Pd dU!X %w`LTV үJE=}n] g^ F4k: >¡mBɖFЏՀ`q>۩C@GY(XJ( }iOt$)ml%,S*$%}==i4$.HHyfQ%PA7lm?PA }+J~8-2ZDІp8*r{" ?U޴OqǨ*AgSzS'Q 3\܍JHOuc9)IcUuB*wQZ$R+8$&FOAXVw7Hݢ+AVh0@X~+ ]5C*⮉4cF̑ hSE{,MNtRLpܴ-U㺞^1J,1T5YD|{K>cmK7r6{;`c,<ր) .H;v(TX`jӍS1@hڄWFm".dT>r39N~[5T~TjcI(ybv|: f.獹.,C6̨(H9zlc܃ YȘjP`A={fI`a$Nrɔ?29M_5۳' ڜUW{n.2$h$`qR[흂cpYH3V #EZ>nPJ>>ꌚcm ;bXTJB;"$1 O ͻ moNj}R BZJ\dTw_GKO]үb1B9;lL$}0 }+[Le ^G@j֓djZik&E ,xB4 k}u 8ISxn P<`rjJE!If#܎)kW0G:4Q9NX uw1E3CI=yI}::Tڬ0ġ"joo-y~cp{cҍ@ΆInnn?M6).W;SWnZLg=d { Uarm\~.4yHYʃg(7J ۹)G%^ShR:~`eZ.u*岏RFOkWZR*Bm\cvi CrlܭrdUtnT:5' S.N pzDa}YRM*L7A4C:ZlS S+^OX Y<2葔z4 Zx2Z1%GZlI#E6;0zFjđ,{gN-xGB]q3\-]u*U#*G~qԟ?4Q'*K$5Zo՚o̭?#95e )$?nkF~dem1WO;iᾔ2ohjj+ˆHx`1ZnrXYlItoҘ?g4q zNm8Ċ2NX/dU tNTVUx#0bIFMK{8܌R++zS@EݟS ^֤FNC$aԢRbf1c"Gb@>ckL+{SH8x`iUw]8]Oa F09^$r\G&Ebb+*2wuEe!\O}-J#,^qJR2[*4s67U'cK֭].H/>0!PQU2xf, FOLW$^95 GSqI›`&719~YnܖJEةm?ֱ$p˕ֽ(Qq*Yhn[5a#v^ZcfՇF^pS\IA#Q)!3ϫZ@j fi&`ɕV1QUbfL w&ivkh \v}OҶcg }&яOΝI#j~WᲑy1h`DѾ1(K. hFt3achiusR\y4Id5HgebW,s҆at"ҤL ~!r6~ojl@~ <Dni6\E- CqAZ\aݢ yZzr6Vp?3թX\ԁj[ݤ2)d|`eZHJ0I95X-HԤZ$>Ch5rlFQFe%܉@~T}@FX6v)X O@vm;jiPB#l9H?Ԏ)35$'?iYO3,3"ߧFsp}kOP}P˽)^IvzU[I@9Mo-rv0:LkUxa8 g'f; ]q&ݏ»#fbsET?z,,v4ވ ZIxG?a5yT}ʠNAjux~c&j 2.=8Lц=-YpjxbGC)IXhaȁc.=@˨C$Q"ʧ$HjK֢HIIjs+`dG)΄ dV픏<*XHϥa\a'>|v>+VV/5bWd1WG4kqhlE&=lQ t~`z=WU;rpEC-cdb*I-ky$Hބ"-,$NI:֖vz?#Y)&vw$ه1N<Zv%uS.G5Y!23tc9vq@>>åsA5L҆Rl֛co]y?'hf N=l0u/TLOrf**+!'O n\Ǖzx5LiCcWIZ KA4$+X1sb_#|Ci^f̖֓v?jFa BK69P~1a5o-b*@ypqw![⧴JLm|=(}Z5r80_y? 4 ritU*;(cT6CDʤc6ӈ_S!:?{};FX<ՙMR0uk. oPv0<ïTZa*A+zd? &K (QBflsNIQd`gkn䊷yXzR@EX ;j94܌ ,W 6ɓ&x)'pz5ZN96Kmܰjl)enV<`U/|#GW O ?|PD,ˑx?74GqtwWRyFk.~5R5c$I.eZbx/. .n\"LP}lsWK\]#>t}BnEEE'Z0ɶU8[§,ߥc䑢(SfCw1n8Ǡ?lʑi**=ZXIpIɡnїV!I!c]#XvQ2F"L(a{SܑQ5aOֱ,n^Z^q+N<]׼_֠is8wfZfq-Kzش=659b>X%\ kK_m ~u2!R9lNAB5-!eQ׬-`Z>jUWf 3ZrBA#3=(W2I6\5hY`*7QJ6HEhDL7)5xsjYyb#h'1U HI10VpD=JvK0ZV y`}l_HZ JRove̷b6T]T'*q#'Js/g$zFЇk:[x#ք 3< چ<|N*0(S p%ٴzhj71/C  ޟ\< #*>j˱;I9'??gt`s cJV_6F zkOĀ,`w\vIb{0v,_KKWնG&8'#^OBW? F= ue {\ITG'o=AoM%N06Z _Rܑ;)U'54t>0j+cP6G7bʑ[k*p(MTh-i빇5*XIk|YGM2D2 = Lcs)I1ֱ'fGac1t3/iI_hy#!E.v* )Ts)?p#ʧlK?(Q}/KrY?žm ThF:.3݉9p尷 4 H0H~ ˣmU.g9IKS oœFk>GK?\v& c袟PJ?šcnv;qҙ0 tĊ3{l}M dsU4c91+FKP'3X7j??ٖ ]w?2ճr1@nDɸc#-#mxR?*qX$;]ORak("u*~M˝BseS8ȕO])E?¿'_ʏk9ʓjh8E"SAcbt~TQ6bڧVuX{["ʏj3. _j 鶯GF*t 0C#lҐix/͍u,t~T£W`g3-0# qr2`?Ŏץ??*aUJ;#Ε `5kp$9'v!<*T(SH,dd}Ng#fڿ\JŌQJ:R2.R*irOAcĤvڼ]WQu K^_F _Roz|'&+5ں.'A EߴWhLƼWQIozx+4hm|טj?4Vq! EzonDGڮ~K5n/AEsy<[o/^eF _Ros oƚn/?ּWQo:A E碛 Cqz+No!uhn/qKԖrӆ\pO3:4u?7`@^IFEfotoxx-15.11.1/data/images/stack-paint.jpg0000644000175000017500000002313412616075370016757 0ustar micomicoJFIFtExifMM*JR(1Zgnome-screenshot6Photoshop 3.08BIMresize tonemap http://ns.adobe.com/xap/1.0/ 8 160 304 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 ?]#LDLvkXВJ<Յ'{E>iS/ӱM7)дᑑd~U~ks 2ocj;VYL+ͼ 6ŞA! T_A7%Աei\Mi6lWcJ9dU=QgV?uD4mjxbŗQ(TD ȽYzƆŀXg/ޖ >-Y;WGAgikzƔhÀq85NT x'Qq՝bNT ?tOi @ ]/2 }nGC P]nBzޜh0O>wsw2"JbqV^v=? *:'OP"k6&FbAǘ}k/?OW4Oi KNS+E6&PLkK1Nd~9W%t(2$ڢ}/GYժXnmȗRm)=1ԶiZ`̼iԑ`h6`hӿ?—}w)+3ιi+y?]? *:'O>r|V`2f%GE3?^? *:'O_`kHE~V`6 vʻGׁ wQNT =f Mh\~YmSv#,3 wQNT =]F }VdҘuۋ98ۓpӭ_? *'Oھkjnqv\Ik3O=6YwQNT ~Շ]o*T)@׭gxnZ:'tOD.{Y ٮz&BI"rqpRڥͶ[Ze$3yy'tOD.FQԕf9bc9%rI9kSD.`hӿ?šн+6L}= kƊ:|ùwU=WHmlop IݗX? E=Qygi;`V.|l#FbRvF}-'w';2'(ml|P>|Q.ˍ{o0\vcR4/@-os xX۷Sn-Eb y$ tbq׶*+q.@U5VIdt'ƤcRFbϏCl\]Au%[Z$aDFsW+(->{)Eu6tn F<mcҀ-QEQEQEQEQEQEQEQEV~0JЬw<`_=Qy@dVXO&OoZw4\ec X.3ƒ4t^;kQl>IGi*@1i*.i,~V ?)3H ipI`1@>˛x[JSVW=;MKxU# X ƕAK/KRӦִxlmd|J\c9 qjmƽ[,-5AgH!an${Pk_4w BԏQ2iB!V\\lmy#RBNIx a}vH?lblB'B~IA u^ 5zJ( ( ( ( ( ( ( ( ZnjkҀ<ǿ9?/Q@Z(ҴLޝY^kL3In"uFdZnÿ [4ڍ$VRs$7*<Ҧִ˩Rh\]Y@.NNWwEqچ|zvRTV4㚧{g5]ߝ&{f[A*81 ps5ׯ?^hF&)٪H.%(ĿE?q4}n?O&b_K|7T_l'G ؗofuR?zյgU@Xo'kZxG=G<{# EzVÿ [ 'w'@ EPEPEPEPEPEPEPEPZsx#Xc‚NM\^ZM;<2eY8AHf݆c4m6ddnU`hO %Mn/W5ӧ1jaVq8TU \Vzѥ˴e< yl.gΩj 2!ʠZ<,YL4)lFU8nk^l1miU0[UupTp:7j'-?ձtAms7$/ ̨֞% s:}7A^ࠑn Gw TZthyw*#(>P7xfT;JOB[N2ǿf{g^kgg O(Fݶ "_J^kuz&ha#!YPW8 V:qBYS %q0_O׆,Z /en#kG^?G%0<ǿ9?/Q@Z(ҴL޷`i?;`?oJ( ( ( ( ( ( ( (uq}1 [,u͹B*- + ssν6#m֫94f#(z-<Q};-CFMҤ> bܱTpzU=/-POK7Ei8oq?Ez_'[?OtQ@Cn=/-POK7Ei8oq?Ez_'[?OtQ@Cn=/-POK7Ei8oq?Ez_'[?OtQ@Cn=/-POK7Ei8oq?E!t^9{~edTJ2[̬gP;EygŗO;)WS/z(?fotoxx-15.11.1/data/images/albums1.jpg0000644000175000017500000004565512616075370016121 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:tonemap|resize| 4http://ns.adobe.com/xap/1.0/ 306 308 C   %# , #&')*)-0-(0%()(C   ((((((((((((((((((((((((((((((((((((((((((((((((((( " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Z#%TU#Ug,c8'UxbKTu#>mR"<8\XORpx%Rz#n"=eS~ >\- ~gxQ{kKDI&UXjX)GM4Xk˂) ґb?YrUm <۽fC>6o33 8h,t:)%Y@4_W=^MWAu*G+0%Hr8?(ja C+ 7ExZxXQy$ӭbXG:ɩ?gJi6Ũ0hPǢB!^=|GYhB[;[;H8 j{GҝV{mDsX84^`=ǢB]7t6bLD=3?#$Y趂VD?]A#%3EǮ m 9$s>$׿u]'*[Q#듊 g]oE&ů"yp21(XdBc݄w=jb!^ ~2`,ɽȮK֩x|85kyB}3Ez/(=?^hk;8o5 kH7)"㾋Z6|Sjahr=rCap{ <运>`\,^B0z/*=h 运 Z`\,^BPslFH1=i',SY#W- >馎y` rQpEzH?J#C,W6EޛVU\/ROV>SɁFq]?G?Vʬ7'-YKn4I0cjy(y) 9 I R[>R4E4?G(\,,g|ҞzuhwQKƙo"K;\AFS]~"4Es{Qn-!ȍn~D= x~}*C*Asһ-4E#i|9BxMemcK_TvWKo-oQȮd?F"PVZ[_[#+6p ?6WWsnHza>-4E#i|9BqZCk6jB$hA³cqMOmU\bݑ4E#i|9B+EGaogjx{cth%M>X?,LrSC6G<ly( z$re A}LZx4E#i|9B/(mly(OR x?ȣd?G(\x4E#i|9B/(mly(OQ1|G+kd?F"PI&3my) qZ"PU--bH/ձګ-_%`NEOƠ:b6o;oCZ ^er\i&91Yviw=Mj[4-!X `ՒLWdUsf$G&qI+meuVc?{u?k,\^h] KMI$s$XCd` Z0j:6wH&$k+Q hb{-GG5Ces3y']sөZ;<Ŵ[E?y,Ozm%8M(p mBiN9]V+E f pTRws~ TKxt9VFw̸Pp1А;>H,,Ջ[=!ͰI͡2o}<zx /\x'm9pzgzM;L"Rn#PYwQk Jxmp#ǭa7_P.ϔzZ[+},xP9$M 2g#{ƖpEuq{Y-I0&#jmVZ_ƀO4k~YRMFW/Eء]נ C[vӃ3!>I tTğ t8V.B# U~i%}R f-#vy HF3ϵPI\ ҹTaB "nf0~,h]/_Ya"c-#BՊ4O$ kqlUFJ kxN7ZKm2\[Gau ;u;诟PWGq€=([_qkcY [j"DLuYm lCw$ lA:w7ŧ4m.14,H8 =TK ,/_$=qYJ2B7x)E'źߵo0u;'ҩhشўon%Rрe>j3)IyjZxL@vuޞ4Š(((zM1&VfE\^\;I6*+z}h\bM/djiV.HNfȲƁ?$GdjE%4dYr_@?$M,zb,\ܔ;J,//#D(ː\`HZ1Rq@Ţ(e G5 %vOs ȅ;(B#ހ&HR p=h~s t( EN9S@ E.F 74O&(PEB.>>ٳ0Yia4q\-)ݐ' zZPte'&ͧcXIs,soh +2+QRékzբ/|c#N ÜWo>o>جxf5y/DnIy*ǃ8MXE{$r޼%ZS+7ZBqX~"[[[R35{t,[$qۚm5O]]>#R"HB2:ɯliw!_ⷾ,[i5(KF$b bXjfjwpm \m 0oڽoZH@[E":8e C˼K8MBK8ea9!f؍xZ@Q!__'1գ$WR# jTttw So[[K6cmi$#*ġ ; ">Kĺu̱tD'O95Z}e)C  *vhgh&jzu櫨瀩Y󒃁կKjQKhMq Mo {s֪bחW{?599," N A}[N5jp[[9UNMz(i-imCIPx&Fڊ+.*RXevHeÁ*'1|tW,|{%w4%F%tuBFH$۟+fG2HuFrW+GW|?c1귥n|fQ hEӯ,帚;9)- tc$ZAI\ׇƘ/`/!7۳9D~;8ԣDQ1@5kzn^M-#6Jʲ{{j/B2w<u\x@]2+JVYaXd^V={-;ijA,bI~!b wQ\]IX.q%f&u0kK `tJ~ (EMAjk^|A)4qwm M,^f>Kc+kOo5i^Ewܮvn:(n\{|Ko.< ed8er}_Z4Z=u54ɀK>\gc:J5/xsNH{e ɶ#ћ97RbnYD>QV>-כIYR ѩe2`pi ab(tl OHʑހIJi(«X1_<1Xxc4776ҰXaV^ko7$pʹ#8de!}yOS֕[Ԕcsd7E?18$EsA6WP! |C.%O k#GZᠷ![wJ16vv_vj6WP<-rIIiHSn|d:ӥ_Rmonn$Gw(T\ P{+Z_ K~a/ksʞ>RP3Ww:Oaq$žʱf^kGYA?of zzûKū%ڄfXL r;1^(/3|+oa yRXDlBgc)|3?{kvUډkw4PY|2=_}RalqW_ɪVKetӤ[ z~Rf Su0_snm"gYcO"hIx>DWr0Ni3C]am]m ²,^>t#ZŶ\hȮ߼F1׭nfcySi BAX2xޭj 5Ʌnm#'AۍPSNh?Dleܯ+H'{WO7]B8j@UD۸vq^tǺBĔIHk687?;G#y lssHg#f iYXs?J5KDu˟ =~mJ(&Va $|fZu]CWw\&dT1sG =*@J?EjWzi'ӮZ{`%uU !l;9>bWaªYf/wOf`21;פ|D-gw6Omp_  xWWSuzθaTm# eƠ!kV[K\g ':wA\4+6I*WκmX)Sm,#jXŸ WYobNv^X]XU$DېHq.lzIQ[z=ĢػĚ-> _Qp:~JcƝ}/t3鴧%!Q@Q@Q@Q@Q@ +8yh.Lr?EH9 =+DR3nݿVF@ۖ?G忥VF@[="ш-;wHHWs_4sN#]sRy$c[񮋨izL:(%eo>DeX+Ȉ+0gڼ OP6],C-آ%2jG eIlR[^CzG(DM kvwSkg#A6#,k]FKׯrDŽ$̓x,~r<2$A9gVqjW)Ϧ>ԩ廜uRHMI>lfO9AJ ?HʎV[xK]JOPk(DB)|=vȴH)LXn$dqzQ{KhpZلJ`+JrEOI_;OmX .N*xP$BYwr=~iwz+%[nѮ-u)97@VMnhSoHpM[5~: :[^_>b]%m=9cn,"iIay2. c~_pZ +^"rܞq_$Q$48Ib@K襀'$ڂ};ۄ6pLf_zjGZPo,Lcmޟ_Zwzby>wZziWK cJ ;Wjnuk넳 kV7rsj^ǷS[76sPaDB24]}G]'+/LWg o$Q W@h ųv#>%hڮ^ǬfQq< 7v |Ȁbe ?0VΛs]i1]jȏ,8#8 y%zN{[D@.Qnb?2//1C&7BNAsq4gY^ܣF%xw6W. q28[|1<tX yw {sUW?tVA}?ҏsH&3@xM!_@dYst7x&t,TEX4m4PH*-;k`l6R~ DŽ/5k+OM P}=Et"G[xBүEMќ0A省I ow+esz[hFhb]|cIږv/< $3*f¨xTu%Ik uYLI?&1֦].qxyX!TC`}AL}.SXc5TGW.*M,0q)dt4KVt(i +<H52oMkp^ +MM}#ȘFx FDCSLK\{ #z&)2[p||aOͻ*eχ:Y*+FL?1(p}*Y>Y:7d)!@$gf3|C4:拢j3+@Oœ#j>!wPlҋiUwd F;Q74ޱmmNuެ8qk@hEXݧU: {T/v8v}_ۛbUF:m$i5(ybx@wʧvsiiKNkO΍de*/Yt/#zoh !FԚR[":Zh>ο<@>l`cZ}Ɵ=0=y8ioZӘ~[oOxv͖`[|ÕQx[(7u t f%i,R$H_sY>"\mq#JdUFŲ ]xYnlRM,lG˷n cϽEkKӢ-Z]C* JۙO˂2}(-lg7FҼU[6Ϧ5q4Vo10w>浵O.˥ʗla-U_*99m Rj^5e'Sx$A{}J ڲ\<,FɲF\rrMtjvX\U]&}N+UV ƈe39+s66|n渺dX}a[|;-8Qh!{l`aW]F䊑 T*7vZ~okQBqOz_/XhvvlSIϦ:_ t)& pB99_u%]g]!mR8>~@CnlYjM1[HѥYK/A&~%iehZ%Aǐ=2=K_j}LmcVc%H v~c>?iVi-+ٯ Ɇy~?/OOO_Ľ*iK T(쳘T%ɏ;64n $G8T<{~O'M-n&iCC?*15A59~, -`xK N ]:[)mvʈ0H[A 8PcP>W+kx5mTZ62ƎP dZ~i _J(1E-6OW:Ýfk[6K e݆̊λғxOYǡɨ8\ykOQa\A~ l}9bf5Q69MlJzgvR'h|Cw%f 6hՈ9#=C7Z jtҍx w@լ-탤;GK:zyZ#o%2=r|BKϲ!:_T>ln~Z_-clD>bHlt뎾Ԗק^z .\ǀ5xFK˅QIg%Cc9Ƕk-=vΉ=oysF ČX;t?n缐E3Okǧ }~=*TVMՆ[ _ĺtiɦI!0+1pĆqFק#&i,м}& RJ%֥6!W;N5O:\6bM>Zxw1BY~ 1 bnׯF+kڌZgvscĉ Ib;touY,e ݵٍHcv=kiyO{u=@ rxj'cf(n?5?ƍ56q/ペ$~'^}_zmGL.G7<ҳI{K:k]+ldg)[]\QK4KgHHձ ׁԾ)m7vEt.!a >׵M`zuƎTEHwPxݞz=qT54)-R+} LqrOߑS siv|{oA ZEՅA-M!bU H 7z|My2[Do5ȽeFLC:( O{ӴRbmG:ɰM 0 |ǹ b7n t{%ҿ0r ;<6h$Wɭk>%m: VAѷ׊H`2AhjŤCT7vI2F[8t;}'IcAʡ(S1$EL_?Gy! rG"HP~F9oJzZ2^4|lL_?GOP9t-[}.q!XtmGH4,H$]68܆.D>V<"&/Ƞ0Zmb!"<(?[(l2珼=z&/ȣɋy'(6,0Kؼ72y[Y_14t=V(="Y¢۠QV"&/Ƞ ɣhi*Lnq }zm-nt>,D"<~L_?GOPx./#,S4GxT1x{Cs4Z.u@7 ;L_?GOP6^g4riKBxU+9.{t}/NRГO{>Щog?OQ<_M4-}3Ni9so[$>q?L.Mo5>L_?GOP[JK+q%4GL1Z=ѹ7N)eyۣytG$v\bI|<"3J<y1$EU4?#Ugv^キZ/sZ6܌NlKD_?Ry18@˦9at |մD44TDUT`:)D_?R8@!"I|pUQP(((((((((((((;)YIS;dWVtjcMr/[#[Eޛl--Bn44˨$nWk:eޥO \ҵ&:[2BUϒTA _p:/Pt7¼nN%Vx Md>sȷ m]7TSC[w14NH)_s=VC4J5xKE>-ԷK5F9v` '8溓P ) ((((((((((((Z#=)^%:3~z 4qhdOK?vsFMgh]Q?vrhɠ OeY8SYqJ4Rfotoxx-15.11.1/data/images/batch-delete-trash.jpg0000664000175000017500000002301112616075370020175 0ustar micomicoJFIF0ExifMM*bj(1ri%gnome-screenshot0230ґ01002015:06:07 09:47:17Fotoxx:resize|tonemap|NE http://ns.adobe.com/xap/1.0/ 141 246 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Rc%UQrHPϩB]`*yS"oYׯpHr'`Gq1>-Y_YvHXnb7DUӥu Uc.èU&ycg.=۱]r Bծ22dT҅ok4aü=GMK:m)eUmw7]5.[p@`\aKu z鷴ڙnrJ6.H @խ{uVǠeEs[ Ef1LukQ%% p{x_Qz/+#߿+I&[Y'_!:K| ]!^95{SƟ47\ b*JW\5ZXӢOX-!aUqX91Wv运.hK:d}ǜ:a}:ZPY.rc@yI;V;@Lmv8>I´uMZWMca\n@9hNՏ],=ǢB·xoemnSiʄ9_jk})sq%q-ĒrPǢBΏgoǮ+Gu3 6v2]ou9G PVvxMcINDcb9';9֋_r_;Ѽz/(=j?:Mݟhe۞~gǢB!TzF PP#W'xB۫+5ߋh?~Tey'YTW7AޛZ]$,dշ cѻV٠KQX?~Uqc*rf4y FtQy [?ey'G`IUYs59 ~cͭ?<$as|=k0jPHB "ºa #1Sk,?*>Oʗ(\&iddAhY3R'$4?*,<+ tϱ,eF!5!UKFg,q9k4?*>Oʋ0ʯْ`C[+8=qLÚ<-v;d>ֺ߳A<$ Sx?Ak4v@K7$ϽYрpaZ"FÌk4?*>OʎPg6Vp.aA.ISyh?~T}䟕atfyh?~T}䟕s7ͣͭ/A<$ m#KҴOʏA<  q=Z,@EKdΧOc`-u.Y߶~˩k/Up~uℳ.4Xi"; H~V= [_CIoT9APHEc]K{Y߶.=UxSF:P`wc,pu8k{imiwzlRPBp3(Η캏mQ]K{Y߶⫗7k^l 7ʷhRUeXy+3|}wi*ZCR sܶ[J˷sozu/e~캗mWk~*cPW^pB8x5kKI׆R5 g\w/auRlm+J$YdG\gЂX> )+k6QY(knSXi_Ȟ*zn{|d[X^bPN?JZk>!o4ۻ͉#i[̌uk^5Yf.$ULhF:} ;t1F+|erź042e"բ; FϪ\O>oM$(c$*vOJqpl8%~qyuw^}ecDw1"H\elQ&>qdQnp?Ux궺[iS.IC#+vWW e?Hxg'2.spUOj7P};tnv4\}lz(pwJ?4PAn'֠վ"sl'Nk֖2˒ 97G[Jen$2Mn#clSjN⊩dغԓ_$UlU[>Eda* #vFǦTP\Zsx3OVC,]]"uR??P76wQ((3g'|8_lUPٿ/.ô;Nt|?ђOmBK((ecU<k{VVLrߥld[mCfȷڇQyvXw?gqz|dAnیZG%O˸R a*sPٿ?-綡T^]1<]\$/_.<>_m<Yjygq3NmϾ3ȷڇQo=  xRMΫFbW>'8?kbŃj.AAOޒ^JAǪ[BA(NG}J-GM,!rk {V( fhmNp:x{n~M}s4Pu? 00+ ;R.$Z@#oΠ.wϴAum V/ˁR1m᱖=_ZF eJcw*=Am&K/շv?A[»Ѥ^M]wuӃ"9]25b?iESj7EwL@@wy<]E^]ngH)󇓹1s8xL/4źLGJN~A9UEAr>Ӯ-[P+G ߞ<݇Iۂ8wu"ڛ9$ 4d8]en Båivй`zժ(+ *Zhtڌ+X–NWZnyfG?o ng>aƒG8 ;ZIdg]663GA6{EQwٛCO- 'ǽ[9#>=VWkFZC(]&h. {{nAzEY4X+ZxO]6S%(*Y;Xpqր #y,n"Ds4LAz xm}aYiZ6DgSrHZ:u $Ho(Y'Cs(`cׂJҵ[=UVS) zG?Q^OůxٵYmdEqȤ}C OM^Qmh,hh2@/]8HW|Q5 l 5O4E=8$U7jopqxɴPoڴiL$ʌ}kVpq՞N&z./>(y-,wUaWVk7 W_mtQ,l6Ŵ}2q{!W*3Ѕ ^ݼ+ ( ( (idp~݇z%=_ 6PT{(@T-nc^ƶQG?¦Pդ\*J想(ha#SZFG?z?TU'7'v?bG?z?1LF= ~(3a(G?œ@}h(((((-a9bvJq`j_XԶuxWGC|U,lZTUգ`_/i(AO\o~Qg7o?(4E[*mٚO*ٸ$t4ھ&acijSptܐ L`׊v¾=#9#yEQ+uŖwaiZ L_(ÅnH7R=im$~Tlec?thZC?7_ED-QtreW)uSBD]6ѤR88;B z֠syc&; PWa N>:{,X* 5Eq+/o!x|6lnʏ3;{RasF9#yEWxZm;Tԥ,LwGQqT?kt-3iPn. E_ւOIo~Qg?o??1u/k(B* zTWn,-ܓ{p=qW4Y߅ ?4Y߅ uh5TF#=DQ鶍x0t=jF@/GF@/@.]|7!׭h=S6"w s#40tӬZ(MFPW`y$qw12yꮯiqii|rf>HzTJӜ6> Ο8cJd3cuUҥE}Y-ūXDU3_3_€)^xwC6MڠÔQ /m n0wZ:/Q:/P6h[DlC85v VV񑂑S`}7Ffotoxx-15.11.1/data/images/stack-noise.jpg0000644000175000017500000002713012616075370016761 0ustar micomicoJFIFtExifMM*JR(1Zgnome-screenshot6Photoshop 3.08BIMresize tonemap http://ns.adobe.com/xap/1.0/ 8 155 314 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 ?4=JBӤKw{XJI8GJE7L2 hTw?AwL(Nլີ\3w> /4^ֱNMEtWM+Nee Szv6M~&$ muMFJg].8H -F? M6];7'ͷ>OKdkGSYkkk3*Ƞw F- ^EOi?}?La[d?-]xŽ;hFX..u`)g>AGNMidhi?ӞHgcV`j~?N3 Dwe/8~=¸ʟ!9jvQ'J#;{`AF}hJ- EenerAnM]im4f;w仁JbyMn׷${Gˏz-û*yO oO&'I7?mmF Ue]4mmΑF(s!?(yN@ 7'@H=wQHgh?<inͺM,رc@-aK{oh䅄jg^oPt>%h!!#Z@Hh cλ{tV0@, 6qS?j Jgo.?<[ҭdKdyar<gj2*SedTx(;u}U\%ub<\sϧ^MB85j(Qa? _ED3]pboG{fUcq?99HsEL5j\dcMTs!Gn[o`% n+dQE!e2z'\-͵z?J笸?O!ymUbEvrap\ƉᏰn[vEIld] Ԩi5$ _k9 yI)#bjZK=]Zdm6L:7qZCh^Um?1)>qIqa\H'r-/XQ&h_ TJ:{[ID ZMPH\3Res/]パ{V]y{5_Gvu,dz6~40GM5oy[G(i76rj4Vs%퐂BBH]pzW4t}VOپoRy#@cDػXgjͶ$Wz\Y$q5ɺ7a1PԴrjPț,:L7BkD$2$s͖y6rD'B#\g#g5$KT&XeYSE`Iݐ8{{%ka =j}0I_JѦWo-C{$2NJ+exl4>m,$`ө'S>u$;Uz@94\ #HQIE 3,#,M6l Txm`ieH\fG`gޢP2 b]A$~U_ͩxvAXN2A*Ƹ.o4kDXr#o^=HHIKG`{ԦY}D-q;pَrkWwZ2C$q˵Vp9ǭZ_Ϧw +3:'$q@kʄlbQZi:u{R6`ZM 5f"twQVԴK;ƞA[YR 0H˸{(Vݤ9`CV"kvAh `9ڹWJvkpior/n#+tHx扳 ,'P52\DW=mhwRh|ya`z֢((((K6ùCj(=d?N+s6]|?N+wDDQ8@ ?4+0LNUԟisRw8{PQc$zniE?l5j.* GۯKVw(`b3CDfv($\,V8˯F?%"W9IuxObJst(92^B֕\,U695(*GVQV84QvH23A=\,Rq?)xMɶ/~3IzP89׊cM `<ѩ#8f 4.v1p I#9 tq ;R0) Ǩ)qH[!YI<ǵc#2Y!T@InZqeVP̪[ }iO d2y$R)WCᔌmہHfSխP\%X,r9U,/AvtՄ}ɮE ] ٴmh!Bw1žd銖.na hfP}0g*ڄV2G} F``q[ |~iOjȅ,N#c[ *NH-!) هrqPt{Fm?Kt<*;v9rxxP|F6=p{fClle,V䌃{t22Q%dYhPuތʞL6Q\wlmT4_W1iWQMk$!A`ßᮚXD~|4$vyV\qK:,4nbPBk7ZOQ8YV9-~c1϶}*VRy'97r8F9ַ`ty"` ;ipOz]K Y-sDX =g {f̽[P0}#=ǷuQU",lE#l<;J k<1˵b :Nh "8%.GSt47W 6:}>-,w($($v' zͲYf׷+D|N;1zŸuhaCmEЄaA]5坵e89"%[-a8&AщI>ڞi$Y: `O2q@#/őN4*#pM y8*Sh.%|7rw] R[[ۼ;u ]V^9i?MڌFn}8?kjcN/j3R(WԟwWhV$O()^jݯWw cqLl2eR9V:U db Rx=֚`j:ƹqow l[ '[Ϫ xf>`H89Jkf/?h^IM]PrcDx.|߹ 6T6*{ 3a( s)7?_4f/?k._ӴyCo2G r[ngˬNHoa<%ޏ`C`dsڀ:|oi^@#W xXr{͹Cjo ^=f\u`xmQ@e2z'lOSc@ EPEPEPEPEPEPEPEPEPU|*՚d$дn 9gzAῃQxbhʒ C娶#'7jC!11.c74[|LEyǕG=I|;L_Oɺ{ )9`wA?>'n5hhM -p08Ҫ<뫉v98 z΀+[nA#^Eu '%A# sZrZȓ 1cr 3rF_&2<1<,0Fʟ"OA52xr6O#im% zW'o?4h dQs4, 9eZxժM5#V鍸:9?#yGM0NuXU?ߔG7/ʌCWl?S~R?ߔ@*0}^}yMK4l?S~Rz?y7/yMK48>U?ߔG7/ʌCWl?S~R?ߔ@*0}^}yMK4l?S~Rz?y7/yMK48>ןl?S~R?ߔ@h5?ߔG7/0} y7/yMK48>C^}yMK4l?S~Rzןl?S~R?ߔ@h5?ߔG7/0}^}yMK4l?S~RwG$X_3s_?~moZ eY11 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-15.11.1/data/images/bookmarks.jpg0000664000175000017500000021360312616075370016535 0ustar micomicoJFIF&ExifMM*V^(if%0230+Ƞ01002015:04:13 16:32:54Fotoxx:trim_rotate|tonemap| Fotoxx:resize|NE http://ns.adobe.com/xap/1.0/ 8 602 1054 2 2 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?9{dzR&yKsnt]^KS%RvIML99-{]NuciqjCֈ2HSЪukuxaQ*P6YIɷ#'+U]Oӳcݏ1yqN $#5Q5}v>O%"˜S}S?G־kĽT~sVPEȁ_ `Ǟyq:`b6qluLxaKq" 1]M'OƧ&pLam?Μ/]ZF?GڇtK),,=>7Z ܫqifI2XmW9㷋,> -?T{ +8(Ii5rH>94s C5M['^Uk94V&KjwL)l\dVo^֡K/N/ rZ=Ǖ4/. |G0rd*6} RAǏODZ~'zNZCP `HD685ZܻO ۯN(Sԓ?ڒz|cCޏr?'hԓ?s ??s!jIƏI=cO1УCG0r?'hԓ?s ??s!jIƏI=cO1?QO'?$?\ThG8r?'hԓ?s?Qtڒz|ROX5hGTs!jIƏI=cO1?QO'?$?\ThG8r?'hԓ?s?Qtڒz|ROX5hGTs!jIƏI=cO1?QO'?$?\ThG8r?'hԓ?s?Qtڒz|ROX5hGTs!jIƏI=cO1?QO'?$?\ThG8r?'i0}?r?P5;PwtRnVڙXkMG'; O6N`}G紐}<~w>?5^QZI$׷ $ _AO+Ta7y+0RFVG]xf0H?ʛ/mЈDJt}&-ڰAm$ 0%,1ǥ~7A[!Tin0g[LJFG=kG۵ΌxFvEV'Ҽ?{Mcİb.+6c(&Wzj?h\^I j e`5>l?&vJN2CJS+6,zn<~qW5cI-Suȴk[F"ky _G,5 1T cwllG"1ϢjrW6Cعڡbebqzk]\h'\!r dC4p5̖m0{wFoz9)u2rv~Tjqǁ<~k~"φwMgm?PgMxz+Y(,%s|r_b^ͦ[XŒ3ގD%;sڤic)XNh:?~y6ZYH|;yhZڻɱƹljρR|#>]}m͎{zȇsvG({P? 5ᛸn"SRk{ͣؔ!i^ZPmI~ |\sHX',#Ꮵ?fwj߇?G#ڇr/H25EQ=KPBUC ہ8j/?|oick=؉or>HV1sK1=~ڇW?/ {ZtSK}Vu Wp:s RTW:N=hu'w;M&yV=ArómpE]ү\:Wj?myV-NTcۈW!sg'.G OjcZꉥKmo<{{cvid1R7)p?Kwֺm}n-!F"FH7q^ko0n%02aiFk~kG8uqcfV\O^;H4)tn!HanE81ƭXr9ᾁm,Ii,#H@UI8}@~ x0jjQR_C< =̱I<a.m9k )o!дyqR1Id c8xLֵ]&UmnnM&-5ti^F9.ea "c\†E1E_ƧϣHzN'2a2m݀Xj|\їI7ܤ7W'cvFd+g¾2Wd#g1O')E`6~W`9=Nú|41/ ;yZ[<* OtKdEuD2R<(T`?ῄŝƕc5;ǺI>i21~bHsLƚ2_\ NFK6"gYSqs%HUG'Oz|U߈;[431UncvzOPzS?r6큾2jz;dq, `A4]J($"IYYaU PIs-FԐGcFgP4ۍZu-F;ȡݤk` G7ğ [M0JbxЉ!!s" Ȝ>aLW;,CQ*/INVrۗR#rqǚcoIcdTi\qPX`sNs?CO恨#iOso[]hqHS*@9 |=1uNs}FZ }B5c are ް5/eXj2:m2hḄh)yNº;CQ-ޝ%5 SP'8V4dsEUSI++S^j)yyooceНdb F89Qns?>>MgP3?FɹmH0yiO#fյ+y,), |*V`0}\o, ).WZV+T-Ig>_2⵴~#f,KּYIHW Jbx47:?5Ƨt᫙+Mq ) C|$&z(&ԯV}ZƖbK;,pQf;?>k>&wg-Pi *i²?sTk˸5MRB|&ɘ4lC#ÁdA4u _麭WLks4{KK6=T7MlVvE 9(]>UW} W. Qiu Mt*3Kve%v|2 ]o.~䷗ ]Ch**c8$3Bi;Z.~$xN݇oN&WI7>XzVu?VZ=fWKbGSHggEUbΠI60vo|AEл^)WhFm,>lc^ecİ.}K#All$Y-L0dJ$r66ژD{8mRqk#Hy;D/2dW)yIk |{4I#~o>音rjvW¡EA`<28O?5-Mܧe#eƥPiO.w<-\>#y5+녒Y丒@*U`]? hzV ,cjizC-s4*d95i+VKhHj*Щm%h8ydO"/6l>|5o_GZ"yw"G0į.ݭ= ~׵yY 颞 gFƿGs]=lowQIu;q] Ayy 3Gmoye`u$~xN;X-vLQ]7]\xܠ~x?_P7!y0xgS[wqm4w(x剃#F;}V6q< I#Up: j 8ǝ;3fA2xg;[j,n H /_VϪ0 A:8 ྋtӯ5+AAw y89`hJ<_i Z]^\ey]0gb̓I[PaF֗W{Egu.rFӐWUឍx[o;lrJZ(|WbȤ+(kku'+âkZ{.%o:92¯$Q}d$t댁^E|&#_B BZecen3ʞ; h?son#6_U,!oi75[;m{$2&W-$d囔sT#rkft!)nL:o^1qVO U5h扥e|͡v>@$ 0y=cgú2548ѭ#4Rʮ>`pF@j|164Z8^0:ɺUr)f-U3f/}gyw7Ms i:3Ve\8AxHmN2!eYۂ瓏\3/6jIOh#߄%30g.zW_o6COXd5]7*J{mB$Ҝϫ]J2bj3.0:?˝Bm缴2GE\^(?21H *V?nay!;#5ڝg{tNcE\+¼q=*mU>!ӚMKwJ8;ڲ5SŤVQkS^]p4[!.f'zPq'߇-tP\0XوY X.҄`:lw}cZA<[/=嶨Jl!\X^ χ$Q,vsqp<mJpFsh/9ch6o!.vřa/0k{H _ Iwc,V! Gɸ9#,09)Xg 4pX_Z,rJҲ\AWa>¶7k$Rqfva/}-gmYHYA_;' ׊"ZjOCr\yQZMT3|m)Z|Ww6=q^b6#'֮oχbk[H-%"D9]  C~p~DCfOtaXQ^3W)ԭ"P40V43X|8ڝx _S=B6"Ơz音nsk7Z4-䙂" &oǑ+Tp)i > b=ڈ[eq9#9[ xh,iM [B#l z`WAE?6\[ݥ輚"0+2^/=CT{c& % и佸eCh%N xC_ xCLVvk3^Tcr6n?F1(s+em dܦ pĂ95dIw^wbG6c6\\[0ŗ{"Ua>P-Bݕėƞ-_ hc {~N5?7}?om>Ǔ:AkF|-^[-{ZAKxdFc'2-,6פx\k?gɬ'Ew%^$d<9 / ZC%a 1p/^|MP16b(/4{d%~"?GmeSgf1 k j&Z  xVuE5it#ZH!7:,9/tUtAՉT\fυ~?XP/v#($ߕ {tj+77_[K{mD*a-w -8|MnvMrtf }nxRGжHbʊ<^^_Q۟5к]Gks, Fx8 R+rxq$O4Y'-"Ȳ:Hг̆021L~{FIxf+?.58Fi јgp(1U'猂iZ|.ɣq>`&B#%dF݋p=Uyg@料Cޟ@(aWo7ZcM&Hmax`A޼/źut m#8iQ;*!FI*fk>-q_[O-ٮ#UcgoF Yn6O~MiP-H1~lRKsc-gv[<]Li4/mڌѫ2߻̞Ԙǂ>$:Fװ=MYUwgx;p0To<Wn!xL4HE!{\l)z7Ե}IéƠu=Jyj#y2GIX\eo؋{'",#X_0ـ~US&iZ[KRitحsWaj7X|ejUԢArIbn>@iNVAxoߟ)hn>u+Ngk0Z5 ܏gh' "rI u{@^$Գg[ cjSP/>=SఎT+%]bm'i.͹da@40;G]i@C=|1"(0# ,101qw5;XO׺אȲneR%:1y=@ѡ#rx"ZIOc)t4΍$1ҩlzī WQkK-͌Is[V`J2%t2bE6Ѥ­lBH7\s.C9Rh]ڽW5?xRR ."l@ NFzy4|:r],>uς=hu2+ 9aaFZa)%C2Tר%dž k"ۄ꣮ccV }6I'KyJ$ cz{}kKM9O2# y YA$LW9ǂ> Cko^"!t! ~ToJ̗-߃/H?RuC/¢(RxQ$WO\,Y[$VGx IPN9$Lm׀3{)8t`{3˼mZh6_zM ݔȑlwJ4/1k> 4vBBĐ3-\\O=ċ,%ԒH$O#'֒-Z*0ZHp05+ r*΃ayZ_Ono<ɋxc܁ִ(U 0)h (#c-WBgѴ{&V{DӕibPd Zx$|/kƃ+}H$ C<0XmwN$ڀ3uQ4Ycջ4֬. Vu#oɁp';zUX˷ЦӦd77}/'y5.47!L͈`>d/[Lj D5 xC3 ͱS+#UҴ馴/XegKbc̊{O>?^M<;&e Ii o1zе Zl:?f{4/[ Zl; GYv>1]2 tiױkMZH+H#|2qBo.C LCVm̽Fn'Wtril$8[OZ\r#AڹY%YeP`!\A Fc,Q,Yt0]άFo G8OxJ %5-?R[YtDHՔwAPl.)eKP{,d:rGxG>)eoȲ]m[u.8*:;gvſ ,nt}K. (ҷ#FCy``x ZZTolt{;cm,ln|ьcps$ҷ->ʳDY/"EvYlq՟coOeEiF $mTBrKE6O|/b5/#Ik]_˕Fߟ*<]BV̖+"jNhAq;A@l`pSƉnԮtl4i/gG>tw w^Vu {+ŕabh2dpVIVmBDa &%at r0siͪ|:FEtŦ(֢F(6nbB@ q\ƣNI&!u+ 2$!#hK'z&g 2mR"ܶ|-qSvkZ߆#f[]K,Λ#>Ww1$f÷S+K.Im_5̏q 46k:jV$Zs_f$+iJ  *w]clmhro!xKY>VF 9'#Z_/Mh[5ͧΦDvui,ı^4PѠesn6,hcGAw<<#6z]i^$vJA8&|tռүjc{ NR@u?xw}~I {ɶ)~Dt%NpXyAkMc| 2y6l+$r>Ѿ$xS4 ylԯ3^DƊI&+xxyqm7 KtsrypHbrOk &-m  khaqRPA} `Zxl \ot>uxvNv{,$[1sojWBaeE*-`$?\Ao[\^JQ:o,A_AB}wwq`&h%7*+(Hp,+GUֶ/nLt1HØۂ:r}ޥ_KqZ_[_/V!l?WidNKp* "?R߭>rEp)Swu ̞"U7~߻ 3ckh.|AS˝mn2 ]A mb{=`8|?ŖV6ۮb."_#oAT>|@%#J+ c>lLIj\|ۖ2APAt ]'߉}.WpG5p.XF]s'4aӆ=Vp! dDc$g_ KA~gmtXiTʡ䝹'<% PОAI'$DĤ¢J| ]Q>tfXZ),h˕$f GeѮ bA1ۑq׊<)ድmbCKu LeY@8S^kwu}6]-,ڰTc 8T4qCݼ4hൾ~`e4d;yIc5j|7~t; ]T kKxK+)]('i56: ?qZ,zj ^:Q@E7<'wj2%*[]PUQ嶜.8;׫ι= WGVo5:iZ fݦj2"l\|rsa'<#Oa-4{E4Ի X])~nG5xJz_Ë40uc3VF@wy\s=+4V}~$Fm3io5mvO߁a?I3E|M$DR62ܱ'zy c3r^GBX`FFߛִ >eK{& rʼwҹ(?fd o-䶒έ3Z$H5 ǁKVH,qˑ.,C`99<kKX7KSq&8+C>c]މ0iVó)s$Fsuּ Nk{ $ծakcu1tDW iYd{2RB/}?V45ω~ުnk|1Ih_Ƨ|uh%uZ;B+2FJ z!㏂I>wp塵]6`=\[=ti$X͟NgCD11(W?_]H .sֵ_RgXE(r"=~痟 RCq-f 1)yϘAm5~Y5ˍjڭ2~bv_cfNOhR, ֞;!/<Җ?EmSNx[֒k Zki*mŷy'q=#r{^4v(%f>zľ'Vi5kfZYOH<7ZY.*#o~H5{xwӋ;F#7XyrG#|q|GԲGvi%zER vj}WLnauQUIrOq.zĈ2k\fVT]sS/uo.XIyiim{m] "n A cu}1u۬#iEn}˺l7 F;Lվ|<5;!uki¥0,LqF:U[ #]Tyt4XG(gcio@ow%#YIQ8V|JlrrM"յ {kB R_~oHT\Ox>mV BgӤy6I˻0=8O/<+\+ .n4KY?O$:ı&֒^iCvAsOb-!ӊ\G/lu͔aT!3~9|/fc-Z٘"B *տZֶIhX7ٱc(v6c}@{%4/.E52UGn Ȱ6WJcm'F# 6"wa0nj޻oa?{}@@$0Ȅr=285~Zɨ$I bbQFFD2|x'.Gx7)-iEp,B z~zMoKkb]ۘv< ؑQ+AitR5Y-&.mTʨ;1{Akx}Z٧@ Nh⸋tl͐Nx.jëH'n.B@4LQUA;V9eP CSm'4imGH'W@s:ů ?MSL}>IeBdc@0@lQkZOޗoch ;G) #?IW>ڏ<9ri2F\VE۸<9{D%L5)/on."D2~+2fA%rx8o+|u,eqmo,L ksZvݵ惪bk9YRI|rem NYQ.MLVV$ ު U~hjDQsomoq%SC9$._b]B$h'p6yN:uPTQ`)h<c!K[8HUG?AWQ@r=nFuXˏμtҊ 0nl~2ԼDQZ+"ڢy-_Ĥ33pN޸qۧٱԶ+njCttMG]-auH$$DHE/ɸā`~j4̚UH?|H#!UT+[$ANkĺ/,nl6bPPd "\bGNwYK suyw~?o#Q,"2\*ȑ@-8mroi~D:E9DK6ټ6c8MyF+vkiFr$2dM:~l4|EV[Z;dE=dإ$#@, !߱*>jKYB̡NMҧP>$[;Ox,k:\ *-ȗ1fzr6`澵G9Wv"N֞j6+ v nwU9]Z|Hβ-5>Ke[m:Dych2!Jbfm^m;M<1sġ-cHccy%\ױ7ocJC `E7v|I֗ "\K4Yv?( z{FOV=Jy!U췹Hbtʬ47¾!Ԭga4{VYI-đO2X >zk9\V,κcFagj.?PjuuwI>tݜBho@Gy=.}J'ح䲟U pG͉z>Yuh!o_yz},Alhm,s:l9Ck+K4X^$0XC.AdsaBs rk=Q\iuu6뿞#ed?wn/GBU4$Ry[nJ0@Hx'>4YԴiO0`m"}2zHUYM%|ֲwәEa!(_F27t$Vq]gTi1,2ns!E9%xL`h_wcaőyl!%UfݸQKc9-߆<_kV1jVqBDKʽp}*'UC"q"mU@uvGԗ%ayF8iHsDQ?z">jyȯ=[Q:v|;NRTQr$@9P| eR<=BH+AhVoukKQ2E#igmR??HptڅƱmq׷<Hq(cqGV&v '! kcK?iF\+n!lW1zh֐gof GH&UrGI+K\nuy;ycCCIvwޛ?Jm[S(Ѥ $i6L&7P!1 k^iZRyWM#y2GXm`wbO:ׇ–V#7qxSi֤76r>ېY%a  O=kʴx C}}n? ͬI04&&b$\[a|Id 0I1x_f̳]IK4edgc//4=mbI!S$k$61H 9r> wwcxkfo5`y,?fi d9 q+ۊ>j"!ӢЮ;f5m#l.IҺZ̾6,ΡXs-yҒc_6{naRK*(+s_5Bǭ~\֠uPIq˷;qyDQʵS'?zM3I.q^Xi#k$1 Y,!֫ =8Kd e-|Ob}-xσK?Ŭ>tomql~PS*߇|{[g諢鷦nƟgn$XBZ?rTPt֖J͠i}b%^XW}ʮ2 >ۏ'fZ >x@Iz!%ÿC]@'InbT;uVsY!Tgu4WV6v6V5ښUT;k6r(h~4eYK=rmC/6s2紅' FfA~.OwW{:~ڄ]L9@>#-vQ_Zs R2(޼s?5}Q[+k*UsD_3[M4~AC 'x$3G}HG<ɚhF %GKױi/9nbܷPvܛFbl; lfƁoLD}6t/ gP%尪  S,j_~i3^\Mق\G Q0_hx8m" =P'G9䌗a#vw\+7!*} "&T6֗:i>bT'Cޱ+zlR6f?wpA94-wFWHоo[D!Pڳ|a0IkN[ZV0V!@=2qo_|Zɭ"m̽C4ή_>Z-in΋"Bs6H`BNsFl7Bᆐ%[Ո|VS8ǵ+kGRM%ۢF S`~2hږcy Ηơqz%& tw㠬C6io|}o6䅷d 2܎չaφW6 Wq[!TuB0wt %-Y4}: 1-4F@ь% h~DVikRYuȑ]a#h.>n㏌R!,M䴗S`'*r\r9a W_i)Yq'(R8!$!3VMx%-?SZM=dͷUa/< ([Oqu ޡqoik[p7F+ O)-`2yoU4֧6[ZSjj/sfFЏ/?x8aTW{dZ}ıI|Yt#&u~/֒Z_B"E 6/-oŷ7.ⶲ5I_>Px6Y󑶺=wYb5#p\M̅#>H \N6EEmol|0' 7aηU^k+k/. )1yT39ؼU6V]y0l4ϸ1PĪF9Jڏa奬jjWZě#@cH<`k?ok^Dm0P:WkXM0G˽*A³q޸?\w4Vq麕ݴJFѦՐ/^iZ߉)~M+v9UG˸)5zwď_vY~O3w**Y;JqnqC$xVϮhpF[@pG9Ӛ.k75=zMP}GV_5Բ"C$Һ_:Ӽ o[a6Q!wy>,xo^ຼid8fG؎q@hg';}υlτlgŵg;H'i^x3Oou_ Q=)gI-&EB N9JSR>!jVoom·)};ܶw@ozI53ZGj?e{]N(cwTWI psYZk^#Э!0gotY;"Br{1@U\Vo-ŌuI#VF,$|^k'J5,C rDLOiPw;g%H+s{uiuh.mq? J4/<5[,tqiawYHXNq{|>OuMA>w_.9%OCm~jOkCbnm-o'2at:Gocus%ΥrCv%ĸyoJ!m4߷<Z/M--)Qlϥ_o 跓]IiSGiG"2* I>+:ƣ_ACmq !ia{g.7\1#K࿅1xjk5lXyas<1c&r1ԡE`E7u $pFMj]|7b}-^jQދnr5R'=DŽ7CEՕ݅+laPŰXxSUNDX9JIM*l`BȄ}iz{Q۪A>b+N;1>g_'\Q/k^t᱂$L+F<*z[W՛XҌfvdO|`g84_3zNm>h%]-x $V7&~f%U[ 3ny,("`anAr=5 }(xs[Ӵ+'7 1ǰl2'6N'NJ j "#9sI _&=CI` 6T# Ԍ) ;)~|=5M?햖C,7*Q"F됱3v#<9k>bA:חII,ӈ "8W wg9,4K-ddU$y3Mh4+x/o&ZeI^<5@Z7n.V}:I&oĐ}iZFoP 0=?*OSZ. uzhRY>abq-<9zdz-5zX&-ntŜZitU`Dڭ&lwQ?}XGSֱ[AЉ L巚Pcp({OW'÷{Q&ߴR1U+ X.y?gG GXɥX/Z)%Jӹi__%r3޺mˮ[7WK6j7cVXʨrp>n8@w_ii嵕d$ʡʕۻ%r@"|'5/gۺDWx,mCo>i%MZ=0۬bKrX Wi:?!{M WvZQ3#%I$FF+ᯇtӯ<\\ ǽO?(5|=tmk),hxǍK%֬deӾlmwG<8"ޭx';ӗZn/.-ϰ!i1Yg~_ q{YHIQ9.'y,· ㊿iYj3[^\۝C `Tk&2q'`AQF+S/O>+f9 [KIRiM${ } @E ( ,zj됱s_5*;xE`ΤH[53|,4="&NHT[3348. [;$3ZNmu'!zGdY|9dhڞm46ٽ|b'ʹPpOT1@~nO^ p@^0T!<Z:el#$H 'xU? J(.-K̖hW|~>Z5iwSte F3Xv{3(((((d8x>k5& -5P\y2k 0|%9{'ŚljCpu hZT1[\v^ySڕ~Kԅ}yoq%gMSj<-*(o$Ķw7߈n&Y-È$I,J2>%@~&|F)q uie-\}7E_}]ƽ?|ET'īz}OhĚ%BXg uگ|Ys 7M.huQ,HcǔʥA靧ȣM𷈯l!4=d$Vd%yܨ7d}6D0jZe1}{x`g QeZmOfh PnͻQ5r-m x RuMmeo%ƫ9gA,~zޗ} Famex%!ܲfK7khmgi{&ºD3\]T9NϯZ-xdt⺺I7Y{pOIp+AB}NR0#vU;r]apZ5?|_X, p`ۥe6@ t a^^XIo:\3 eK+ ~nA#>7V|3,,K_? s!A=j`Wudu p/I4 PYT8s('o"j/+K4OH>ulWOT5Met-B#m'%\pkPSJ]ᐝϴ1` `V~!RZu(iCq( Uwd`>?_xIѿ$_b CV82m@I]GM?&g-.kY4+yɃϕIt|; \ͤ MIb&s PcjP\ՎY\;b\n*%1qW~'_F֌i7i)D0ߖo1Ҩ[_NXR֡X^yLQ '+"I7-vc- rtWSE{ zܘV`I*XHvt}M{ќ08K-Q@Š( ?qZ,zj ^:Q@E7ڹ_w3j? )ncV  I|[T0+~mis4Vy*rI,NsOPet;#A4 ϿdJsTJsUd>ұ%??%?18( ) wS>?@tV'$|.*IO\UmX?%?sTEbJυ;P ) wS>?@tV'$|.*IO\Umɦ%FQB.rY?%?sT"P+#S@wC@w a F6$OkC@wC@w q ߿MKhы*I> !};Q !};Pwg;n]']l!A-́M*?I*I*5#(  )};Q !};PHMv;VG$?G$?@BlُlP UmÃY?UUjrmܡw XUUlyc9AGB&$??$?1 zcYUUkC9,β?$??$? }qXUUmQXUUmQXUUmW!c.?kVHawhI( ucPIv3{6?ڰg~İ ..8`J+~c 8qU< <1l۲$ŏ_R(O}O4cϾ77ަØ2SlMi&M77r1eϾ?2SlMvnoQ > ghO}O5noQG(s_&Z--?}?iFqi&L[?]r1eϾ?2SlMvnoQ > ghO}O5noQG(s_&Z-O}O5oQM+1caHSO֎Zk5dV(ñ}tv7&[DK1&!I]xieqkՏ򪭪F s)u\p *~y?O'[oK i3%O,0p'fV>h;x 9Hgk+WC. ^Igd麝GX56Vc;@_ŷtH%[ I3g;t{@1hPny&bj6D \TE%ZA<}׬5KFXu"$*PIs)?OI2~HhE4-G`84N{Oq $/тws3 ~N>J.94gm ]MsmsU/f/? ?-a߱9dԣt{}Uf{S_&h1i^%TIE1Y ,soe#iW~2ߛ[}GWm kcʚwx??FױI< TO֏'h|4h3?生ʓyI|TO'4 {TOI< hiHzOiKƍ^o'4yR)?ZFP3JQw {TOI< ihM6'i $|?ƍ;*O'hR?4j?'h5gyR)?G'5|QS?Fc;ʓyI|ovzԭ6+0'^g#Pװۉm@|W 3Tsi?%xlk3Jr0wCg7x ,G 9InX+,NJ"UVokzቧ6_F#hmg]s߭uVzTBr27_ƳG߻7ƃ~x)݊SS\nAygi;1py d~'+S[xWu o+SahRMg[猁{KȚ0x `{Pt]K'e]#}''|g_s:'}$VvDVHFwc5|[?x"v֧ZGW.FGاjq^k=sӮnibl2)݋CkO;ƚ|Q~m9?{V`wpְdHw 6 ^uw&:.cJ[LңK#Ϗ9Cz6'B-5RkqK,qkoAf䜜NϥuI6 bQl,D9J,k@zhΨ?=!Q=!S "?PPg(?‹YG3?~+/hΏG}P? .ȿ?:?T?5}@? .ȿ?:?T??G},@xuCP? ?}vE?:=~+/hΏ@ R`_~ad_?߇?Ht @G)݅?:?T?5}P? Wad_?Qȿ?:?T??? ??? .d_?R`jb4?G#?C}Cg( "?P?G},@xu~/#R "ުQ=!QvEq֐j2{R???@ođ`0' pij)7+[Xøf_V7HIQ6s i+3sSnHyd_iu{xH"x9kU/F g-y v3}xK+9ݕ5@ #NzVv4]g_c%"K 8ݢO@$`p= b {x5.{0(u|y\*։4eqi!mEՀbI8?Z1[<^![~$fsu0op0N</^A&$h$*CHr3Ye%iޥ=ȵ1=3wλTÎ=ODmV푙{`_ U Byvjֱj |u ʄc.IR³Ki-~ϱ3$o}̋ry.ΟR_o.MK-?ߠQdf.?]st ꩧZ템$q3nldF=Ez?!^O|2QXjWfVw40Ġ~X#LmdgEyMp^(+L:[^33oZ಩}LXzW F̗Vxe.9f cwg}wZص&nlJB }f2DZ̈́F[Y/Q ›w9c&>e$rk&EkV.\33b#f?jv cqso&8˹rKc}(Տv֑th..KeY6V ;=8ks[[\/'pLvJ㪡W{K+QO[iZZXBn ߝڲ5Zrg ^Ry@(۷Wlh.L[+e> 6ӛiX~V%̛y=Sm?ghce3#-VYFD|99S":PjڄI4qyZDIyc3,l}FtxCO($Hp͝GK/3e3c%;+M{lӮ,t.9.$0o; ~^kOWQjڝ6g%)n$4JekX `7.zlt{ ԗ1\YcfxjaFNIqSAKד[KӦo3džcs:Mm:O$:mx׺WVQ;yXrrZD1^:xa=+q:y`)SK =ĶZ-e> հm_>f0I )3O}Q%TFB8bs|u{o_HM"{)W1BLJdy_ݦs[*k[ {MjLE|)<8g 71'ӵduH7UqW7wKHo&ZI܁n#!weT= j|= 3Z6kł>PcA1aD',ĕ 5k^-/>٬Ewuuo< ӸUsE]~ͥYӬ,3cE1Ҁ?U&-G]Ҕ[@3JHo#cV>'6 v8Yc/+ʰđ;\֡P"[K 6,Fľ`i,<.0qE]X-JDo!{` Ad>oatt(gU  Y7,_NmBMFKdt 1r|%O:}l6ymfV nK,V֬w6ZO#;#7 ǟjNU ={Iw+ 2I i4:K)%:uni6D L, .Wo s0Yeh<e›rN1qE],kmsle}A+t*t׭MMz,8fߛ5w7'm']CKV%Rɵ@DC%iP EDV0) |i 9E]>Ysä67ٳ'KǼlqBzlP3B%#Hcs<M;<uf׾x-|b!Q͓'=h8k7R5" {MʰI5vY&p@ K xŚb7Ώ-k { #*$FXs֜ "mQ{EJ+ :^"LqV(E&$^U z9^$yuV6mD>D*^¤;߼\ g^p0ʖ^w&s-蚎id[,/ʀ\GAc%["=RjEj4\D$bu$danNϐwXuhj+Yx9Ap 2@?x+7[(< g;m7#jk>7ÇK."y$2*aPɪ%hyoiڒG`x&x٤ X;omƻ~G,-Vq[*Esl]&,nj.džgGŦ9RYch.ʬ"FHP=T v5NῇvVAydm\9Q'e.Nqwdd{}83ZUPeAr=AP2Pپr% 㸫 RihQ@Q@ q|6l&3iO(h1I 0O 2rYhkٰ}E-YRWEU*1,Ϳx|:ƛϫXA x, 69mW~ Ix W&M%`m2a;wJ.ufmx帅DBH"-=r(ՔIxڌwit j쑌q@Ljhwi:E%̒\kmiճ3YB=x}>"߬{g(ۥ:DB)?(Pc _x Ly72ˬ}=T.HcQJNi_Cwew>zfV_p.Cj/i`=q1L!L.v , #Cm7zq} Ԯ [{KC@K[vp6?=͵ֆTz\-w z[3@SUgkS%wv6pR/n>dy^? yw ڦ{dڕmuY7,F  1u b6.Zp]X㌀CysG̐C&$hHxrѪ>3p:^xĺ~.o.K-Ƙb)y~\cpzz_4KV[Tծ α+,1?#q8 pψkTs#IE TF?9VZuKOUVKb\m+L*1m>;fKGudm OJ NX+p;]/׳V.דQt!I:U@~6~4Үt l`%EsKYhyU`yh-SP㺻H)V(@[prqĶEsk)bDpXb@ۑN+>گ|E#څsnRX/5u;o g&%֡񮡮xt VV  h"r dkӾ#eg]@m.ecG'i>/կ$P]>g>am`hgLepQ5Kw=XbbID |l dc5+DŽ| i][=;ܙßKa]j֏m9n{4$GenBѠ'p:1}[WԮeMwM4 /#9VcC^+ڝ.;ۋHY!ĒA!gvX[Ǘ3¶C[J3B7)W2 }*r]x aqA}B4呝 ܬWn쎜VuƗ"ZYؙsnUOX,0x8gZgou\Z>imkk o$XSbE|c,w?LqE7_H*خPcOĺƏde2sߡD9Kx^PkomVy ]w &wcǵ;Қ.sKP()})h\Kѯ]ur=nF-NpESo5L~Fye-ca C2bRvYGװOc;^?/4_ >uqcn|2ޫl6B*쮅_ |c_].{]5[1VeH|ٮ+֧57ub/H0 tBy\2}PK4١lBLy )aaUrFF1^Zxrjɦc12ή"%Jq[̴ؓoJ|t&*lnZ.l79<.[ćHѴ1`֓J^jr6Ձ=+ң<-a-"Si @#݌קx\4 M:DYTܢy{/3R0#j~u{2)tv>f2UƑ}c=%=&w7N9f ; SȎdXD]y,r? l4+-Η$Wv<4,bsp'~*kw,7J}:K{$H|L x,3Նe{Akq[l8+5x^炼E? 5e*jw2K Qd˵Vbk7Lˬk~4:ݝm!%T9"5>1xT.CL'Cv)crҕRGҹ/ ~77n4xrdkM1izD:M4 y~+cT0^?@z4>ךe!dQ'4<|#O }k}6Kqj56j@ы"h.QKâ.3w8!]1rYzԾ'|;fL:h&hFvK#G<JI%I "`f`vps֘m=/)͹fLAU=dS++¿XA0-{g5/8SJP@'*r8էxJѭH<#m_Mw.;1ͪYdeZ0vsڪx-ŝjl\*p |z6~-oq=i,_ZydLD50XGi3P?lR;h>5{kyҨhb8‡%㚟T/՚m/N륬mF F8BL*7/4h;=KImr!1*9, !~G,tD:T7Ca!u8B]vx.Qf%g<,,~Є9i6֋G[_YEwvbU$X2~1FzV6|2LG->lT\4KEQ̑ 3IgP^,DP$?6zsU^a^hv-ZK}D>Sy`¾f}Zx:ޤ N{oӖ9y3sSIxKӦX"*dYMT߈5+(5&/uW,$22};dp?)Ck׌'6u;r̊M-`m \t ]P:M `ddI!Lc5uŶ x&.ŶoYr0! &M|EQ ΃ծlE-Tɓu/s/xoP+c+jݭIJZ>ı9' VW9otѡQ\H Ȁ,j+RZ{ju崐 `uZ8I#<GG7~ ^ }Q/Dx jM7m; gָ~.l15vahϵW$` 6sv u=~[? []daym0a `Nkuy 8c C 6>)V[jī}sÔv Fs/Oտ[.`(dK!N߻GPZ}{{ F͎Y#%$#B51j/IIxT2f|T|E? z>j}FM4lpXĎ:+χ|O.4m":$chm<Ǻςt'c{fq4BED.)灜p*? k7^;#zZIjC ,Qk vo">٬h]*(.&i'[yɐ*ÃsU{C?;/-^A*̎#onGmFKg#pyy"7dP$?mС<'oבA*[ЊXV{|̻2y$5ӵ3Qt:V/ 噌dl._=Kk?9MI币pbv2yKnsFX蚝wQFʳ,̱<}F[F:זi<{lYͤy&嶢y,6ߙ\Vͯm+ZY̶iS摙T`ֽz7Zj\w:mRi٬6qPAe|A/Rwnh^^s\.":kX:IEsuoypomUOʤBWGiSi[+ib^ochXCsqڤZ6Npb{w.IRrqZLV|1 [hL1Pk |7rcUҢ {9x<<1;jSþ K{.%y' #͏7hLw{;9< @ES8S{Iu[J[CP:h=NQP2̛ԬE.=2Z4?kZpڅ4i$ӗȨI$EW^ʷ1!8>c>9 Ep^M8ΡshJ*?7H7 >-QH0k5| !ing!@<| +j1ufV'bX˾% Ab9Ϫzj>g}D-9-D[#RO/5ƙ*}o=Ǘ &R_|FJʽF_gY)gHٟ [*];?#Zέm%O2K-գmlJAUp3d09QL V&mo)WyCҠE&k wo 7UG-29h9*X8ա;gM޷iV\ͤ-CW B0wrd=0+M/졆Ŗѷ^ix,m1ln _ &% Xo?/\;$6r׮N4K6_ o `V6tT$d<](k[mxkӷݙOޏ1~rHgv*-xsMԭխVb,."/Zw)$f ҐOi^jq.o ި)+.aq[ᯂM|WwwG͸7E^ 1[EƦtE|Y-ັT@RAWžұzµ7On<no9w.omȂ(8XىzSux=CHUYk*"8T(TNy #ZkK $B3AK""p7Cx//M^ OW-B8|Ҷq ${=71j`.]7V].#j }Y؞I Y= jךd.@5ǖŐ<9͵8=%k{6{Cl~).$0o;no گ 5=WS乆;q,Ii+A)-ec$?6GVաvmi^j'nEhIʦ療,{95x~$:kRx Չw9Ov܌o],k];N;ƽ!ӻv1 XI?g !e5 X픈w d߽ܭr5t=6yi+1h@2FܔX`*mWVgPXܼ־zE<IE(BWisTJO+{]BeUL]5-exq M3bF8@RGY-otSd(oX}ܒ,~!ңДmY#!ܐ w|mlYMqqKu[iݓ7fy-%:$Ȯ~l2B3`qӌt -W E]6vWjrMM221\FR⦸ZմIXI&|L"~F FG54kq}xV= f2NF2WB.Mlۖ4Xek̥e_#3HV𖁥:/I+DF5H.v|3U>j<OY\\]Aso5{ixLJSZ^ѭϪZ)mT<5$:H6ڕ8tt @oy~neխ㷽 ^B$*"Up` @Pl5AJZOPQHZ;*Wh>Kş n|W4ښZ JL $eGs 0^kވ|?i{goy~T ǓiH;.-AK5G,rO3Aҽn1ivZXˏ!c.?kPTw:ҊJ($œnc9~|1|K;k"h%R n<2.sgfl+}j{4~bTmp2Mm@0 J=f74>=6fm^O2C4Rl~]4y9LcI]hD 2$R1O8<'|'. ֶ&R+?j%1|u={gIuzX4s\@zHվu񽖛fWٷ+4+YrA!rTOn< +-TF2C*:M!?3%]W%xt]^:Jk<2G`l>s u:ƭ3ſ`l-u%>cn>e$hx.7vwWC-%1 ~$?zͫhZR\MJc7 ; 9{ם\kV[C`cuf6hƻSvTǀ7~"ꪗF;DGuk & 7Bs@_> ^xƷZOv1@|K"`}Wҹ߃=c^_2,W>ZI1*ά孭S>7P˿QΘyU ~ #,y8S%ВhV)tβ%#%Az=o:w,&.6|vɔݵ(Tj"Q-O_.*僣ws^h$*}6@]wS]3ZMpV#oڜ;nxW#|6^RKdlZn ?+ʂ0@"v9o0xWPѽgﶨG(DBh r+{,Qn;67i@v6j^#Qcq.`-PhkA\V&Ԯ5% .)a \Y{|xWW5̈" #!+wgk? "j\ im68RY6A*|Y&c<\>xH}qsYk,y99CqK6ٙXeL;+x}к˂]7yۀ(QIp^W 7zRZ1>~ڋ"HFŔHx9YDݷ>^(X/,oR䶪4Lr>i>P 8>OK#X/.hiMxcAmFp\UXEޘMqٔy^\cpzmOP4}è_jjz-̬\Tp9yq=[E( IF{1[ohZ Wd~93f= ]׆Gc#senPlP6;6rw:6|[BS$ݬO P;ydgU&KHQ\tZkN\40d̅3yfpWjX5mBPu<0͉&Ql̊+2^IP*O>6t]6+[Z5{/΋lEu[*.۝x&v_4;Ki4Đj`G\8|+K^! ctKxLq Veݻ/k#ώRVR?e E签6PF3Z^7qhl^1%u4<0r3Mk|Fã[-yj4ngiHoLu#ྦྷ/Zx~8oVi[3y*~Ud ,kT.K |kӀ'öBOR8˫2HAԮpylߋZ.˯E\R;&Ee.dR~Gxվ4?ks}U"X#I#e#l~2GNO#^McKЯ/$4$X;KL\dV#@6nF}sZnt.>"ApLDV<~CDP.ټmN n]U =p>Y+Gp45V쑴%X끌{g}of$KGm,mvkm'UTq=з}n.G-R1]\D|#3Q|LI-acoP$|[iWjΩ?U}5ӧuyC# o6OxoɹK "=JS[\aP A`8^3Hn mBa6Z\_\ߔt,ʈppg^F:_|kuΣLYJPгJ'N-ppFEC3 | X 5PZ آh#YcFDxd9Ao1)@9$s񦧣\ G~nf;]k>>3 ǠWָ߉8K頊-n%ph0F' ]U+y߉>%_KҧRA;塄#Qc4Դ( ) 0*%dݷr+N⧃xWkidpPO;U_4<빮<[_v chtS ɷ9¹|ExQ5nm+#o6,l.|5i}+iwj*$f% U!luxZki V!#0QtI<3hEaZGlBª#r@d}iokt[9̞H"Ti[9ː>c֒1b@J(Q@r=nFuXˏμtҊ 0nm^Ga7k;˭j+K{Ƅ(%l0UdC^>~.xY^^>+.]#Z{5-bX-l犂#KFiVMg4[$1 C,{#^ms4,ku9fFG;' %g$O=m)d`i,ֺx. W:,Ժ7/tBO XH5orL?zݕmش[ ԡPsK4_:qYv^wqm7&g.+#%U^/ׄ;z{ mڳx6mӹOZaXxY7ctfYTcn6P~,O%᩵F=Bm>"I w`c/ )>Wľ=ZGC$Fx`&\H49w[&4Kb#6&b4{ }7YVgJM`S;o?0%Wր/xI,A٤of#o-׉t rBw`GlZtO?f_ K[j Z峑#%Bۉ`xŽ|#aףYm( S70Bvb31;O'>32[Ķ4#€sT>"iחڅ.lK!E]I$q*&k\bT'/8 sY~&VzX[[Y^PF[H9Q/i[GE6?xndxCȷNcdL|1QzɨiZun".if2 }3tzzTSmGjzputTCeiN Տ%4 +}rjZy~β~yw$⛣8>u;wIIHxb 7.Iff!H gxe\ɧ6zgaA#q?沵^M6}+Śvm{wۃx=S1#TyQpr?Sj07yֺ|vvV @ +sWsYr}T:{qv1mك;nlo4!<.7# կ4Sĉu"#\bB"Mr85B$x.ۋSlgXPpϏZ>jzikv\DK`1d :x7D4.5Ew?dKH1SsLʻrwIƱo?8<Պ3|g|?yG5hĺu^ݼJ|\/^ *ڽVX=THcPîDW4ۈnԯ`Y- D2ppIyO]l=OQ+ǫqs-O2X%mR@$s?X=k,]v;kAH4])A-H*#oP9)0:/4x]Pe~p1xF:W65\5ƽy^Drfp1# t=+}9߰i%:=겙Rbk783\LJ>h?Y`pB?9l=HmiٮE;iwm<3b9]WPVMcXYm5+$h-| ~bBA ۃ[a3Mӵ; :yϱɷ=5+=V+MK[K+{q1%pHךN/[QҌwKu[]Lc&3^G|;薺U0G0i _˕)z85 Ù|Y{MrGRA*Gin21);I r+ׄ/|Y.ڔ 1'ʹi\hϯjZ>𷻍wۺ>h"ԭ#"9,7F$p1+ |LфZSj5܈Vsjn_T`^m}\\G=8eo-`'T wU{-BPYZdcr>Պ((+s_5Bǭ~\֠uPI:v.53MT*Eje(#.06(M{E`FAl^t5{4~#X_̥mAc9#qY] _tIt;{QͿ'־YvJFʁG#WSᯄw^(^"dg(@,`08:<=xL$hu+f mngX/'v@EarrA8 >7c?pQߧaoUvW,AzqGCδ?7-':nLabFV.AP5 \jIj2F߲) ;lېlG˜tP>ԼUsoTKogU +jď4"`lq$)N~#$lU170tj!Q:ٗB]ΰ3!F@dsz_u,B7hMNR#.qĹgڽ?IwNi&7cR-zZ#47ya#>$v߳O5!ɗq,Jc>ό'][1uG,Fb rp}zq^񋏆:֛?O_^Pl@ :zfLi;H%ךNwF͓VQEQERԴk׈឵16$mekMɆHmbxmºڽ,GF\_kOog1Q ʴ<\x@5?$_ Ė{e֑RW)nUT9`qgEo}'ZEŴ"Smbڀx%C0$ޟ61][Lw&9eM``! ols>ͪi]CF 0ˌ'2x m5˦6Osݲi6b6!K3㷖OxcO;jݝ)Q@=kGڦsZEneY"O69ش3H3ЅXmċ@?JV#CB[uT 2j L0򊂀3f| ٵuSГFuc+F<& pQ2Lpν=`ۺE,)K67t_oӤtxm-Q5vvn ғ on]J')촸ZVd}C~zB+5+^b]佹g6~x͡IegIťBnm2eX.*9Fە yx(:-Lӣw' yci; pGםk5kKWMUc۴QpvRNvmRF,YӘT]Uܯw>ԿNj^!X[]I.Pd)IQO'Z iFOԡJTHB˸X !e}plLl0k$B0@Rp:7^|Z}Q)1y /{E9m6@(73\|^|=u=ch&u!$Y|K9 @G4)/ոUɄ$3y!U'*]br" QpZ%EmIQ)w sֺމb'8B W$5X)A$q*eVdeym'@c_|%%LV]'̺Dd(줂Fqߥt<xS{kƔӆ'|Tu$ ]}5")NOr!TqƵkټGOLpV7S&8m#4a5sOoý[^1Ӵ+}6K$hdvtuVa67zVzuݒ [ٶ\(FxUU<3^Wε.$xm$f P6 9k~&Pnc,Av/%5JM"+ `Xg mf+;}%uc [K5Ј#>*go>7Z[Iw<Ҵ7aA9ҌM\Ks_Fsi*Fc-/>.\[Ok O̊$oGL-&_MgtSJIo<']G̖`wvG͌q<-CL00{ .(ʞ 1@CsAuZ=֕ ځVrÃz |qntpC< <!Bp}:Q ڢ@㠬}gᏇ5`ooc?"% |:c$-\W:gmiW֖[iz|c[#2 ) #OגCFMid(Z 8B}~R1^'=>}OU[HI(vnJ}ׄ4+nt⺐Mp3xksjYI}ۤdXw/&*#z_'|W<~TKyeilZfy-gŰ )H8ï%d<(l t/kRrу"c {P'/<qjjV['m& ]hk:x;8'IS؆e9+j9.KmAmf̒Y m$ lp zݕAͻ4Rda5h{JT kvHњkX1MF1cpxTZ}skqsamqqjw[,* BFW ^(ȃl@ `lxLVP1` 2(hȣ"L2(ks_5Bǭ~\֠uPIyZ<>𝆈P{U` @Řq=r\UUnYHVZ4?$u|A>7_'OF?jw G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G gy'֣|? G g$_ғ̓y'֢cGA>7_?$u|2OZ2_ZI#hύ?Ƴ9jzYSjVmiZrḍkH}AgPil&S Pgܴk& ,{/|\袊,#y.en?E h>q<'p z>q<(\>q<','7袋7 z(p z>q<(\>q<','7袋7 z(p z>q<(\>q<','7袋7 z(p z>q<(\>q<','7袋7 z(p z>q<(\>q<','7袋7~p8X+xmN18.(OOc(.7? ? o?0Pac( l(0*1Oj= 26%|K!wv2Ib:bhfotoxx-15.11.1/data/images/zonal-flatten2.jpg0000644000175000017500000006563112616075370017411 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-15.11.1/data/images/mashup3.jpg0000644000175000017500000006743612616075370016136 0ustar micomicoJFIF0ExifMM*bj(1ri%gnome-screenshot0230ґ01002015:02:18 18:09:52Fotoxx:resize|tonemap|NE http://ns.adobe.com/xap/1.0/ 530 307 0 C   %# , #&')*)-0-(0%()(C   ((((((((((((((((((((((((((((((((((((((((((((((((((( " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Vc5TU#U,c8'֪w/."$bWR5#6o13,gyb}I-KX)7=6&{I:#nUQ-)^.$hfTY` p3;wVu;g䰴66Ra 9# V0,ĩM:. ;x_W?^}MCp\̉+6%p9x担;F>cfFS^+h$<]w%ZW]XE`6pb',=k5]\ 4"đ2͞1Bwj2gp^ |gWMOM_QM GܹJ~*[I"e 1Иwx_Wx#M4H,|4N<k6fmy 5 @S", z/(=~7lm ڕƩ-]Kځp%=ޚјn?:;uaza=ǢBRWy%&/%6+,k\EqwzMҬD??vQqXB0z/*=h |运 ZcA]S&A_p}+؟_ʍ n,>k%$n95rO7Vf6 ght{Q?rZNt-qa ssjI}/RM/$YgV8ں؟_ʎP8cV$ZuS+- _ iv^E-Bo50 :Ww?Q8=+kIY-blL5E)DQv?Q1G=kgbq*6'.cy֏8zTlO/G(\p؟_ʎPZ<[;Q?syֶv'bq*9B7=hlO/FTro8zZ؟_ʍ p|T~Trĸɵln1FϏ\ /_^WZźZ >+Uݸ~U`2z;i-liN|C 1&8mZv9(A<]ۢ?( G _>N` '=z{n5dxYaV x 6>SNzU>$֗a4!#N T1]Mτ;[ ;is+ѧx:k/V.UUw4rדL p`14_3e-w%ΒE}*\`5ϮgQɭNk2@ʞgoVvzݎݵC&wcgp#9Hl'b$,O\OC_Pp^si+pH1%`~$}gA ue$l.9L_xicfN|3!ݔ~+OM47J}6Ei&ۺ|q 4ǡEsƶp| P~2/4V;;nIRt8g;BZ~j}o-\lxۍzz]rM58gGk,I{Kj=kw5Υc2+4A 8kA[iNWS]xWNI) Oj!u -,r\yAXόdеoJE坠KF 9E瓟j$ީj[y0m"Kzٿ5Էv^dSEq1\nxǷOK [3 MAgy}[P:l$6#는|BD,zl֯o,B[]y `vg_F7>ؿ{n_wUWྎ+ OpB/?ahnu 1ntaoiZy ;yqy\pN:g?>%m[Kkxid.08j/ĐW Eo3DR1`s]6]D:eC VE|K\ z_?w9]fc-R66sH"r_ϬjwPMFѯ-$IězO[^Үk3&(;O z&znCs#';T1!G%oo|EG5;X_XKO0 Iq#?,u?H٩ }\28tWo绚̒H7pxǵ%Dž4KVkPn.3#Q#/ pjY,wew) MA`S46k{h'8aI,x cZ^DHN a4te}n'? ژUʹ~LO*cw+G`_KV~rl4=6[<9'ZҦJEgVmo5fQy$g{ֽfkKmi#dАFk=0jH3!9R3vCMy|9{wK :nwe$*0Gih2xK¶兩q|b>n޵VbHs@׎x:9͈h>-)6ȝTpT5f_jQHoh#:K[ zvj]g8e0L#c[[չQb$[Q3|WO ž Mʎk280z稬_xSXc#CwF"0ÑF?:@}OWJ0ONZ?u-7].'m?hK28-'Q-b &G` h Ĵi*]F6'ܨ4 jESQEQEQE٣[ d,H#;B׼=koshIFA#޻*Pqӭx'6#rj6M$DlvppHWJ|-hq]ntا&xcD*s܎?ZP<žѵ.-lXJ$ÒqhOJ?z=׬oN p'QG`yց+.mReNі̋!$v/P7M}?N훙IbV89FqXk=B 0j1j޼q&«:f ^[IK+A,8rpHa2?P~"<|'qu \$RD kux"My k"oޣ3=Ae3̥e;&;{M Ex{w<օ|Jמu͘[PFyQ?P=;Wsjl? /I!'_^mCNu:Uan#P؁؝?g89}m7RFKd-KV#T;MQ7Q"pUk< k*ȟEtM4/UUQ@P=Š( (((PQK1@gnk;{]8՝ݔ1ր41F*n뺏uAG(TȻ~t7_n.bUtk@?87_n.bU?웯?7@n;߸?\0wo 9u 7OȻ~tob6~L:˸ 41?]R w[HpqF E'%8c$*q!_?(qF 71'{EQ( 抂k+9Y22̩9j Ph((((I C!8^Cb[,Ʃ}%-wڥhqBMN'BWŴ V@}>}5]ޘnJ$H6r\ݯcHjeHU_0k'BDѤэZof쐷&i:tȥOPI'8kv?E Kf2tsyL ݲx-.!T'uR7m`N;|ϽT+ .h[G4?PjZ֎GZ?tVԵdS䖚}4)$ĦFU:Y睂tNjH¶#ăaaF㿽O}kѾ"=>ZA[ݱ0:.jǼ,,mcxA-ϙ$p9ɭ䚑(((((h\w8}+[VWhw"9 jxO _h^Vp4n_x _/!~ zO7x4 a6qO%_܋CٛY ^EQsoyeèrRp($pG?m-;g[)P.?y~b.R 4C68#%uw>2\FI +9f+"<׹+Gс45/,u,\g#Ui<}`ڎ<7q|۱p1銭}Kk@Qt*0ۏOwJ{4/nJrIL`}l.'8%Q"=AUk^ ҧM^'IP SN{%-w(mG(A9$^Ѯ-b% ZFuHGOF}=LHAOlkKOQSa;5{xq4c3vk c6qZ@$ lm$j,n<ۑC,0G]H<=2Pq-:͵0Α)13(Rl0;ᾞlM{;ǽRv à f,]Ȗa%X,%˹@پ#hQZMk!Kf2PdNⰢcw=&xՌ{6P8특a;k)yVnDv &nEz<Ȓ"6.!LjoN-yR*Ƅ((((hv 9K{W!:Viknnčġ$ lǦ;a3Yv!nu[VwRe>ZD~"${լM0F]lhR}NPBr𦬾0W{[&' ,Ӎ`˵[uZL^ ;{a3`zh=hZ+<~_$Rir v|?.Q˧<]I (RI"?3ۈiWF:rwmTn}5MFh׏އ! }YSWP)VoS{vfldkO,%ۍ#`?kҴFTKL7|wim!]H? s־jX.j03Ǐ@NqԚjy'ӗSӵفd\ Ὸ i5!==^S_X4RoUHVwGnkFͦ$4i$P@,ēnjO\[KsiE.-hjB)y W7Oq[I4]ݗ%9$Ԫڒ/lI?.K, \52sɧ"_ QߩR ,so;@O( +1Hb w>]IPçz4-K`8}8*7_emiC], 6/oۏzjPBcYdR79puSHbQEQEQE٥%fH]$T?i܍B ՆOǠ^+Kj*r0Pk-|-onҍƕu&VWIGKT:/ xzthnѣ-7n]89yǥcmm."YL~FY$V~7?g7lm1zJWYyk7$dU2_"c%3I$v;4OOx֗=r-roc{C&%$6O>C!^2wX%߼y+r39FӜc˝;SEH4Ԣ-.ЅBc<Ϋx_}m E̓Ɍz^a\ҍҼz? ZKkKץ ^ݵF :bSZQK&lEr1#-Fݏd}(} y%ӯlmu'+$|NFOE [="M=5u,䘴꯸7SF~oomyLk7LWEݵQ~@-O IQC8"c1KsR6ycDE^@ݓ-ZgD'˖.Uc ץhj6mSZ4A1u^cx&Njw-qkpok*J̙L'>mgk.sm v S"ʥ =SZ!~ cww [I#}=Dl~ljWT> c4PJ(((((E.IMGG8GV>>fҍҀ Ѹ ulY w  .z1^Ziqqi.D$)v[[M<5o0#zO֧}jgB&n!'hyTd ]B5P+GQހ,nqin>Y$K׵k]9}u;i.o&1W=I< eԲEkuo4gH P0ʒ#@_Zom,݌9 zZ[O& s Z/u<#&*̡2+wpH\ g,*SXuJz(SGMP.52JJ-y屜}LT~/@0rC4gA/En&_*K&ky%˃y$dԔ ( ( ( ( (qͬͿ|9-u{[I6Ύ2FJ PHBV<3HWd5$kM,>2 Q倾$5[R&Mv)3([y_o/W=c%|w3²_lc v|p9sD5;85KOj,1F*Oˌ9z]t4o>z^x;㑢f^ƫcVK5[ocqb} _P{Tc{[#2$̣9>jVv7'yWq&l?늹cq e}_Ta6OC@[ 2I*+[.Yfx["%'4Q@Q@Q@ I#֏dQ]cw188UFO Oچj^ ӣ5)_Łkw4-t q(b\QU_8d$U-:[1UƈrJH=z$4d?:G}w~tQu~Ӥ y=>?:Gֱ_OKU_Ohi90\cH gHy?F]R:OqzΕ=#隅Eݺ5P'(>?:G}{~tnu>IYZmy6H @YKTKMCLdgMFvAیuj6hO KlXmb2 {՝Bv0C#8 M.薇# XC3כ~&9>?:Go _Q ?H+kV"|vg?ƷyvY-kGtMM{dSZ_q=?'G?O.qF(îUN'=n'Z?|z"מfsT : gHy?G]P?t{^o6aSvTW'_?O.<]XuҴs{0pE \?:k}kr,F;Tݑ@cjVRAK*[RVFp:Qx\؏346(( ZޕgeegW**AqTuΤMJ'a (WqZY2NKG5 K 툡F$z֟EWQ*W!R+f'2BTwSmnܰXW3'<=xQFGBF2H#Vz6 k k7kLXӿ_LGWyw (tPPSbKwq>"ӿ_@fG*| rnBC66?GZBh5k%G< Oayw (tGJ2yu8VN>PjE#,S!>8${ =`;RHj^<ђzݻ*rU_*,+BMίlD2Hsxk_\_ϸ aPO~خ_#Mӣ}f; ز2|v'lfIΝ>i:W_Kx˒pOuuc^3u[Qӭ/Vqס沯 sq6"^#]k)TP߯k@K Y4Jss"D|Z}1FL\+]Uofo +23X[%Ao *Kp8c4Hu;58%Z9>08*}Q񗆮{'RҮ4rxˣ)R^ #>Ni6iq,@5'l2}M?mhO\Zwp@_;D=2jb_OޟixPZ[,JϦE6mo-e")C?RJN?mgfV#ӁR̿4?C@^_ZD&w EϦMs 񍧈=Whc3! 0 ]%e Ԭ ˸d\k?7o#Ii};uWDrpq.z\) Cx#PZԛ[UMPj[c$b!oY''ȶ8'|A4qkGHdȇ o 돥]iQ[_?iBIR05'8͜ijs4TN P}-Z>SOF[S.o- ߝlsLuGwaky햖3͌>G.J@>⤘''M,$2ZYZm-JL*i}A{{ma}u 9 M E2k𧌬ٯ{[KxKNrsJ5 >Q61IxG~=\I-մlF9lxsa J]ې~DՆiN@:M&46(\њOk㷷BI#mQ֬Ed=(4)$S<+q0)RA) [p̫sr>Ӄk(tԀFћ _RET%¨dFwmŮM)$pV☋9ߥ3wA5K5@?(ѿ:?[i@#R;7_PFdciuGsHu 7_MatJs?ƛ:QˠhƓaϺj _7__Q#Ou gYNӬ&%x.Nr $oqrMF8p:4[93Vuicsqʬv@Df8IS`Ґ?CFn&ƨjZwt_jvm qܪIgxz;-?KA8Qyߥ3wA5K5@?(ѿ:< ?Ɲ _@Ѽ6?,?€$/X]63I4GqHu mF1}.c7__Fn&ƣFFGL֨.a'n_PdOqhgnqoU*t'jcYze֟n%(HY@nEQEUf|ymi+*G; 2Cj ViWG`ԡ6L '&$fSh\JOK ?'eԷSB\ȿ?'e %~*K~;-\N7ٗ8 GȽۧhO@˟@iZ\HnH^zWe_/2E$di?+d/C3_ҧլTHδ*F9\G¿ jtR]_ꚴa/nI*0flHFynȮB\ȿ?'ekыPܭ}/^In-Q)ᙊ =++RHwvwquk }>so vMpyIJҼ+$zZ56| p>}:F>4d~ҏ s"/[ѶhO@˟G teJ߬B-Fa96FA֔NdV̹w tc;.^M])Q9?Z22E$LY@ ijyn7H1ېF p#Gj-xĭhO@˟G tKWon{ol-YVF/ f;N\w!EP0iS ėOn-&hƭ =3H~NxI{еv츣zu[mj`ڔz>|93sW2Y:&KmHyE뿄0=(Zt0?=b\񞵦֑hŭt}$ҫb $cwT~  =m Vߙ2b})W-yL\eA=I/zsm.[R'nj#3@跤1|qk@PdsPP&^GsNѸ4dELv*sZ.5UHw*+aXwʺ~te]sPP syoӿn;~uWbj갘my/oLcMon]ڔUugJVIZ(\ 9 UGgM5&Pj*ߘo)B8޸^ {yaMmBr.HPFFBi2iom4 bUu-*Ėl33qťыQj?)J8u۠PBdwPP([n=B}>uq &THsR|&o5O I%̗M E<\%iov0z_cs*uACA[b1Ηv$UݿogNɺ֣/OUSbʲ$t^ޘ=Mon&vq\Ӻu!2Gl :ɺ~tdvAmz]RNg/7"o)v9?a{i/x썓k7?jd/Qqq@_o#ߛw!fq(GPkMշJCEPmJ{ϵb[8ۦ3zETKӤK 6^23Lҳr,st19:~>oƯQ@hZj;=:O,!?ƝG?a:O,!?ƣX҈Ljv<0?ZΏ5:.Rjk-!O.X{py'k|typOR,K3b;BbIViQEiQ_'ƀ1LĿO|S1/?F%'hKOщ~(3y?4b_'ƀ1LĿO|S1/?F%'hKOщ~(3y?4b_'ƀ1LĿO|S1/?F%'hKOщ~(3y?4b_'ƀ1LĿO|S1/?F%'hKOщ}%7y?4%Q@Q@Q@Q@Q@Q@Q@ *'i^`TT;t:(5aPQ/9GF5㴂4}>H#ob܌gjw^A%ohv%F>imn'K?_rh+*A(;dsڼǞ3'3A% sݳ)\}:ץ+N -UP؎yZ(@T1dS/,+̦Hqi?k&'YM:=<]֩(fq@z#>Ǩ76ucL;FFN9lp=%>!uyWHG+I<'ϲj?e(&?_r\K+1]CAk3-'@ q־$z{I&Xf`d8`/͎stٍŽ1\R, $`pT=Q<B啶c8'_$:L}0ijzwtԂP@z}?2욏#|Mk|@]HN]?GuJCF^i:JCCy[=욏#>ɨ7=Yyh5:rjRnv2bq=j޿0ډҞc_eB99#;tڷm; .UdJ|gI=ڥ5 qʙ $< 2jcI5QEQEQEQEQEQEQE(2[건ñK֭&)1rz`@7> ^΍o<^y$, NWF*;zo6],܏P 'b7`Z ?(2 @<_yuUȚEAѶ>k]J"+" r¢2[M݉ҏ ! WTde,nR?Z!kڀMǖ# )+EoWUe#XdtM!Nt {8V5O*-PPj:+Ěb8-$ CD޽}@?(2g:kKiI_}œŷ {.]NM`jY.%Bt>oY=:'Q'Od h]L;Jȧ%T#/> VT%!ܥF*kkk{Dko ؐ(*GUu*꬧d 8 456 766 2 2 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;F" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?+(##{RJ\$J6ÃސڱulWkd µ@mGTi;'4pdw&XOIeː1Eo|]ՀAL}jŖj.cmn]F"Xk Jn4ѭY-,0X~fEtm6{9Z;^}OJ-Xp>B|y']TU*3幸|Yf@=GzśG%uO*Fم#i[#89mZdiJ g*q<P$:w)|YGP~K?wY-|m$+r>bG5Ac K4H^UCHh橽ƣC rϣkH|bwWǮTL7Os(D 0sԞzU{eQ]^ZI0G'³,l[fg6JBqk4v" iQFR)rnb?Ohϔ5bg)/&#|֓!>2k\M}j}BdmߵW*Diyv&f?]8zNj”Sf:6+MKZ7vca]Sſt? Wp:羚-O2z#+?KRtiO.RcfwZ_\ǎ~rGt|t'I Ŀߩ?ŠFZ9#9tx|'G.ϖ~ϱF >H< ߩ?i?ԟ]y1M#={< ğߩ?_]>$-/IמN9/]#-/I ߩ?޴Q\z.<'I ߩ?P=hq]A??ԟ]GiO.)?e.iO.\#-3IןR旲`z.oϖ~Oor?ԟ]yi ǰ{Iw=?LRtiO.={sпs|'K _ߩ? ǰ{Iw=7LRt"-3Iמd9|7iO.'LRu码Kgg-3I _ߩ?Թ9C>ZgO.4{8vz./Ϟ~p|'^}u=@ԟ]|C>zo?F]@ԟ]nDh18?J7ݻg3ykg[w"A@$t'#=*?: Qk؝y&Uxd2?'"__O?k?? -4f Xu{.5 dXOS׫Gy{{8Z%ۘ$-g-g_9L@A[uُ_9G"? ʌ,6w;X@[u߈El-g?YuThr̥wqG7*^F[9K [3.0m Y?W_쿕K$隣yƮPUqyر9Wںk1Q[k6wFw yf+yH.>H$ gpFȰ^9&@mO_>+gm^?ڟQ>̯q.$f+WKVEN@6sAWP<-f:?Sʟyǿ"9@,9H.g'8ڑ$ג$W1c+Z1rk20UO 9Gխɨ:?WOG?k?9S8hTrd?y~)>ɨ:?WSWQWQ8J{-Dן»})SDY#hHr0A-g Xƍ!0PIY#VF9k]ZFZy ?xukyq[Ix6fln=I?poN)|HV1KsNRH]0ӅGKdRfiKS ҊnMSiњi QNRv v\Qɢq"a\mQv &)X.0F;an(N!1@AJ ( LFeXwZfTg4Q`~[kOֳC_FZ~ZN((((`( QL((ES((()QES.?_ʟLi*RӀ?2W`뽰&J9 aӭi|HΧy5(KQ9C4M#ҁ`H f)RM@L@-(lJ\SDLR❶.j+(iW#.~]+mHVXw#+I+HE&rq/6n2^k @ϋ,u/K)DZG9_C?FZZ@]SOx}Nȿ>%1[jKhgOB=sc4^%Ԣn[o0*qt=Z)(Xlq>55*נa|Gзok撀1ņ ֽ sdhH'MϧX_ `c8[Y>9Kz%Oi I:}I+v N fqio,5|XnbvL>5/]8>8 ,q t@ç٤Ok:Gmh8M5ދ~O =J\Y\\;o-jzk-=u\l,(((((((iYYC+SaЎ]JR0AV#hZ[4 'o6zq(n̰mgҽ6 |VuA@ EPEPEPEPLY*}Gs&TKN&J7t?ӮO&J~7?kKFU>;mɠ v+1NӂN'TA8JjFGjPW&¤@0چX(ght`yygh<_ch0xz?sA 7~S[M=yo ׯguh]ڷŜjKLp14JĺXa:? wulmx̥.>f ZHB/)$&UX'G(YW*+N{$֧VDˈ2QOW9Hr5#9Zώb:S'1(h4͌f@sA8j:wU(Z7t4}W9 9ZaN;m.pkIyQS8ص 5r蔎4|H\L҅4=ih4((GzW_zaT 0iL*&{TL @£j;L+6Z!aQXqQ٢"4RcVeRP1(GC#CQ.q.qood;ͷh݁T+39i!d+8  q޼ȋƋ-Ȗ\&mOe@RA8#\1$Bqp%o6SDЈZ@V5'~\=Ij@2Oġ` E0zx6eFE'|6=qV_F%ٗ;vq֚17\lXAoBO@*͆ &F3o#oL{-E%IBIE-PIE-PIE-PIE-PIE- ր9|]'ϒ LP6XTUh ۈ \\YQˀNOZكE3ZKu#p늯-``[X"AQh8qA,/&MWsI 0,=qZZ.5gFcA}g*HjimMH FHn "]q5vyG` ~|^B?k3oN uw?i֔~4E_P8 )EwNR-F)VdjEZC&SRQ-J!--?SSL$c#L|^[vEN*90sp=럶n[ #{Gc5][Cě 3;`=?ORZ:R1/w} zyPf=mU[-ZPyR{x SːmHe)7gEYEuAY Q"Lۃy>TuwQ[dm8<4'w>(E;<r:@zG|١&?MKt[ L$G=h1Ĭ;u䤛[x0v-⨂&'"yw{1e(R ª麌ZS -J8ڂKT0zzU?7# LvH!g">os]iJ.q7ypϨ+ [)%h|'D?^G QZQ QZWo&7?}\D?RE/dd;Y[pb}G\Gss⫫XV g!Ev6tJ+Q OrV,QEaӥ/|`tr3:B?k0Oo뺱'Z~7?kKFU~yQ(q(LCZ`R jEJhH2KRS3dOOhrkKNwK$D\"z):u݈=+B_6HVnB1 j&9ؑN0cޔG4WxSM?4@0 < K) jVD+TMY5Vl 4N4PIKE!"SE>״?K6[AulVzG>?0״?zGXa' ]CxeO,!(#NNz{u@mxV6WIW'U=?Fcc'94@|1rIM5|஡@ȯ8Mϕ,-=z >O&L3կr'cO1}GJ$\} |MN@׳ \}PǬ}z.9"-Rq}iis3 pP:8xKVumchǕXEW!aEPNZm9h:B?k0Oo뺱'Z6:ڗƌ,Z(q))EP z`dKR-Z%/5"KRh*Fj!NNia◽ zixxZWb۲1\IJ`lV;ɇCXҫy1zG4gT&<֖S=X%s@QQ]J`eli%8f#d3@QU??y1z@j2}j{:C$vIY$'*?A@7{ѻޛE8{QL(EPNZm9h:B?k0Oo빱'Z6:ڏƌ,(q(S0S2XjE52Y2MB*E5i:T*jU5ibU5"MH ]d\ȏ $7ROak.Qv_$cl<[ˏzٲ"lXqجu ҧ*漇XJёӧM#8یz bwI9B1A.uAm#Z1FzeeȬcfY[q"XsZx{GiXǗTtNOj+;kx(QVg.qR}ϭ׈)XEss*[K+5PHvXA覥gυm$d9#I{84M.[%Kh)Q>ޟJuZjֱ1TR1vH䴾c˒oIt Y}1Tt[WDҬ4Yc#+o'#s 1=z%ibXIa `*Gb}6Dcf63Q%3tWtvW {ա֠IdG!^p*ocjwsZi.3!B83Jծngeyˬn“lOwom}{10+ȣpw#5J<=iuoȧ=RQWMu7m:Xo<݂NFEAu.6g$Z4'HŻ=ΏehywV\.bq~U2q<وgR>`z3@{i✤βFp3ZQΏb}m?謸o4зc")2*:=Nj6Qo($MS"鬿[^]\[=s-WF㧦kWRGJH.8PI;Mb_ Rڷa?erJ 2H\hq,p۬4k! \#89%H=36y&?;J|^?\.}[rF&$raTr^gZW6?hTĞjn ]cR\Bd FPCdNkӎm\5teqhMz`Im?=iObV+ڂQEQEQEQEӖ3/{_IָN u_ӭi|Hʯ,)@ӌZQIJ*5:?/x3\OƯZ_[KF(.r1@)qEN)‹ OPhi™p4dY-H BM&S©jgndq=*Ꮈk]>gʋ6B#Jw}jF[`[8fZLDp/-J)dsĞF[!5t.V}W-^5FZk;D(((((((((Jd*(,IПOlgٱ~ɩs?gԵi;i8 2iwA4]o5\Egs/0U"$@gQA¹Eҁiu$0$O-aJF}rAxMmXX)rۉ7& Gh$ފBw#s#49EP ;8Enw\]{2"vAj tRA5b]jzƯ>-5JMs-NEݵ{e˳stKos>ɨރjċĐIt"6wQFg@1#Oݩ-M:P1Q _8p;󏨠p}=`,  vG1JZ;5:/{_Iָ?NĈ˅-WaqAQER撊vZLSԒ!d&\PI74+cHY3ǽB ZxBvr6<$'TWDv'q{`ONryđ^!=i-ErOn FШ%Qpr21Mh\#lsqbT+[̳F'!-,r׎!HT`Zu2 ljM%[8njzQ9 4u t :G, +*9G^9SM;tTA`3j0swpOS*mkqֱ..嘁))ҵ G{*:#N`haO¬¿gfYo6 *EYJVV]SP+z@sEM)# )݊Zj?1^״?xc((((((((j^JL=6e8*zd~f9"MRUiL 2xo?*{H^mQ_zprz1w5 Ab=nn>;ƗXԁ_RAME L/nn>r>nqkc&dE?ۭ-EX"[1Mokl;YKqw1pvT(&Qk NKcʲdY<;^~yBKP0&d?7_1WE\;*_zFdlD4m75Ie0&J6W?\VL?uɇ}4k 5&cTV 6@>kMm76_O]mV-mB9'_RB?k0_o붱'Z~4:֗Č,)p(9R` P()hTw2V98fk3bRR&u!ў#ր=i1Rx#8#20; 1Nǽ ,a1'yKr01rQ# P2jUi:(5ˆBD,|!CJh?ac5Fs89 X8JNS2! ]+T'EHhl˩Y;ҳubJs@V!ޓ?7QF*&%2.LnKXbO5SLNjyCDoZ 1ǭ- qPnn+#FGZq)ԝ- hWOsLWE8\TX(hWf7D̕W'|B?:VkCȸE~I{?UP?+g 4m{?UgߣP2ޑA[?OoH'x=*(m? B_=*xޑA[?OoH'x=*(m? B_=*xޑA[?OoH'x=*(m? B_=*xޑA[?OoH'x=*(m?4 B_=*xA[?OsH'x=*(ni?4 B_=*xA[?OoH,!?Ư{~G?_TCsH'Vj?_T}{~@?4 BۚGl!?Ư{~G?_TCsH'ُxOgߣQ\x Σw5_eY0#Qpk0oo_26yPmFx#ל|g7u/O/Ra%-P)qER@cy7_0I reݺYUzZ܏ iEsBsRDަs֛nYˆ^O` ֐ _=ҧF HV!pq@$I<ңtCDl$ҡ}{@'='  6&£#9`h~XC1QZ68 8 3000 1000 2 2 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?**bR8yk!fkl<1ؑƻY-s 88?>xD|kAkyuyvC0oÏʼn+KPJU긷d}gtxN5 .?mkXi<1Hu+ nn%Hp ygxS&Mo x˜}^=lm z/^4<#I;Y`ܼsL 85us Di'=H?KxPֵ+H,82@PFS>8 />[qlS7 JqҾ 9TKvyJ2iޝ>*|mqhj,F# 1̕FUUry$bV_ڃTվ2Z] % đO+M쨻pg5AΓ Z߅7x{~&Qf=[D;Ԇ 63ol|guWS<]D'&B\(+ Zּү+uZE±_m.w*ݏz~i:: O[N@]h[U',@Gy"?{/됦}kZ-4Cg ܇r3++_Jڊi=_oLKx{FH{ @s2H\04L'_?&wj."{#V)1ʬpFA y)xڮlOGQT% !m\_22ͦޏfO N{|Ax[636.EBP^|T?[\D `+p1}(4eGKTֻn='WHdK%b?wՙ?l;NxCt{{!e>}(]Ҽ]uo <7 W5 ,>`L1szPm>2(OO?"fFOȣ"QS(Ƞm>m>2(OO?"fFOȣ"QS(Ƞm>m>2(2)QEQEQE?<-'-)2 \i:hWckȰWRG 5OG?Oq>>g,`n PN9/^^gkj`V2fYOQ׭|G B[?'QҖ#*oVc5܅8Qd p~^71xOFO"t$OI7M<ײ Mҥ^G9{^1o> 7M.B !ĉ& 99a|JѼiCJ]5)ꈻK9A$ =@57|'mkW׃HB6UYfyW|o L]/,r2О{_R[DsG}nW4m?|6ψ7 ZLp ] xr~&⯴ >xo0>qӽ}9cѰc|_Ě$h\/Ke6W;xl@9^[u]hV[?C'5-rYm$ୱR?)vgF'34Mx++,w>h'z$"exکԣ/̸ӄL$|*7Aoi_۽-BP?j@;򏎟 "|%z+ޟ*y vBp!ē\ >:Ԇ7o0Z[yHۍGOz`.|ecV/Xj_|ei9TxIbM2 v3>N0^W'i-FG{Y_8Fu$y=s? 6 cP?dii+5Ůx.Q&&+W1e'֨$(<\?*HӥD\]_*ٕ/$x捀S~4]z!1;B0;ABoL}k [EhFQF8v?`@ <JqEPEPEPEPEPEPEPEPEPEP{}L(((#._*jdmK̞qֹe?fo1ˍs\gxľy0ì-I:`~:G(yL$*aT^C2ƮJpzVJk>hm}'7Eqm$'WI ໎^oFk]qJ*QX~(e3N!c=khox_ih,zȰs9Y'\?:6i_J_Mv 8?'鶺׉4{ ^{{AelSEs/‘xY|F$AfںOgg5?|%qxJM*..yPPAZ謍:G4]RU|d̼q\_hx/O0oQX#e0w8$j^/ZV73n1=h +O7zckî.zT [P$/boy7x+ʋzuƩy}Q6 2DAϡk65&^ 򤐶ZhS+?ڷយѭu;H>Ѯࠫo>-[doK.Iˆ`J7x*_c<etj7ل[ŦR1szҥ>>M[_n_%i1}$shIai66:)r\/uմk7zK\c##v0k;zW~~-O*]""Cm+݇A޽y\ڟ,~7/h!ݡ:>n$Ѯm**1 z^Ɵ&.i/xQ dCʩh \,8LGM–]CDѴh]FsAi[Am"yrʤ ?g [X)zTP]PNAJ:5TGJ<ڪ 4OB|U3ǚMll]-$R @6ݡ ;k;W7Fr\hoyon;nS_i ~Ө-;\Zj:-ٷ e ) ~ t;?K[۹/o뛻$| Q@4dVF̺Ҭbv@$;׈k:u.%4~$r_LadU{  '!-9?Zh|E[&ͅDkk{<ǍedW|`š7hM:$^;5+6WI-٤m,h:0s؇ xO뺕Ʊ4Rnu Sec% UH>2O'Μ4@8-@uP%&;/|z}v!kZ}Յ<+wmvDe=PjTm(C:xkZ.-|yΕg*ݳpj| ?eڕjBR\ZI=B8Ó޾(%ǽs>+u.lm&9%={d:;ggP&}8vחxGzAA6t<kƈnIINeW7a&nn[[B4P?!ZL$2{7Oú5O  ޭmb}M؆\:$~߂ G-u}^Sռ8Q;IG+HD< "6kazNFGjYg?;*ÿ Eh%[d(ǂQӎ8bc s#4)H%QEQEQEQEQEQEQEQEQEQEQE+}L(((nK:vn@;wvtmv(|q»ӮӁ5P=6IWH' _>a|C.ntԽdT|A8|7iv--og .'J1f |\lq~ ]\Kk{< θ_pmdMFϫFib\ũ:M'^*]BMBw+ϕG5). ˬ=p>t")Fzֵu  jZ^ax=IJ9aE9 bqmrKN"yYF96XZUm#&i8I,j@]@_^v?Z/ [qd2d2.?J{y4PW>ǭI5t"Y4Ob"FpB`ýb,񇋬3ּsiH4MLsdVy!TdC9WGO׭"tbtl 0emnr s7_OÉ4 JSEIb;[I[t,ܶI<5& }jcSZ,v*@;s[*|W!ǏW:t|nPץ>RpDEPmTt_GkqGo}sw7CoHĮ#޺595 WJWOM3HإY7c9}м5?dM;L}[ RVW9t t.h?[wxKNi|B>rs:? xS<;{{d_%v[rjTwƺ?7_ KRΏ 8:>.cvCm F$gd~_zd:UA. |'&܋d1gbX>Ҥvg,٘[$(ȿh_JktNU 3Kcg_2Ov}xqqj~%]z'O 23_wzW/x{YKQk[V%VH`Aq\v?xC#Xxr }GšyҴkLڕ c9`O^y44>4M|Ax?e 6Pxq^.|[''5hciVk",0W_oÝ?& Ԣ^cvN|'8k'fuDžN?56$fo8ɻ~ϛ1@!|a?ߎv@57MEgdCZ7ޫ%܉6-N+Y2݌Ŀthڢn⾱_5ɚ0B7nO">8v7>ݨGS\o7Pɦ$AG)K_)>&j]׉ ?_ac}.(uڊª+ֿc~ }UnGZfӡn[Hdp2r'T|~m~)WEQ`msrKEdunk~5|C<!7|35}ey5 * Ү{{m5 $ղI?g?׎'RXc^q4oR5 Sf3ek8Mo~{5ۭF',V4` k?Út-?FM3(Bh( ( ( ( ( ( ( (SeQEQEQE~bX>x⟈7<+xXCt'LiD$Ȼwg%=:M?OcM׎kt-u>~o0#Z#w;衈ES91jxrGL NvI<< "b#XǩⅩl>LѷdƶsNkR>RPɧzxm2+_yu^.XG䣗x_!s5)otY5KZP(Fe*vp}kAW'E1(H@1h((((S(((8M'4dShdQMFE6vE(OZJ((((((((o)@Q@Q@Q@Y46l9M)S%Fz:u* Vhg_/ _Ez-&ҧ="X_f ^-'Ǐk<'V-búw~/ WYx8RX8ZZğ |B.xIee|f2YYAAO^=<[C R.cdB Ubc HjJ04We x?i},MΠi(gyr ~"o&kψw}-C>)T9=ꍗ?/|Gm[Om!%teR%UgPIwO^ijS6HJN9?c/hFX|kͬ Σ*\JI(vE H5CLK7ַL$>SS2On[y~NA菄r=+~K<𬷻5]Z5TC߅. \1/)v8gï kI2H՗}ȗx^rX;׍g|AN+.?#c͵ŨFb$a#<׈ZƖWx7Jޙ o-=@ }_Oڛ7hiC9,O/w7G8?Z/Agw[  @#|y T}驯kS Bsh."&!@!8 {Oŏ#THJcЀV(M͂@u7=Zi:/Rfec3j  xMQr/ލ[Yu_3]|(ZgT_cV*Y2m_a{q*Śuvh-Utu- Ϙ#1%܄%4 ER o!k͵ DIx>hRk>'-t]1!}u$M~{x#RogMZNjޝ?QJنaxv'e@kOI4o4k^XLklcBgLiڏ|m']|GE2c#VlX~Ҟ +xY9\ =^<<=.4?OsឫKnFH%/`#[b` FGWpޱ*|<ς46+][YaY[ T)0gVSԞ({O/LotfLӼ%/MԡLd''8#9z ZƧKN@\b O|G|mxZ@z.i>qOfIdt_N+O,g'$[:f9<  cݴh_ßx[T2mK;o_,[8` G=Iп IWZwtm)w_]4X˞|[= ǟ+9sZڙ]3X"t #e;9KWI񯌼;xb o}Y 5xV\%NH}o !qiEfyR(cБQj_A֯0ť:u׳[ݹ$b5J+Hj_<_ҥt1* oT%^!}--N]M#rAw]>ZSkRxJ:\W_a7)!ui1.H$.qޭ] ;xKH^X̿$tgpxuo#hwm4tZ|0u;:A+4&L/cğ ψ\RMkJhZ8fL AМph߁~0#=YX8ϵG_ uMT֭kI^PHXO@s3_+kּZJ¶P6M$dQ-1ctʒ{WPS-|=+gtKP5Xc. ('OLPkZC'+1Ou 8 u$__~(臋g$5/ڭJ6dKqloٹ[h}*E6((((((((((o)@Q@Q@Q@oĚ^F&[ioG_AUyxK:ǀ H/;@fbOدty{o-WglU=+,l\ |6%ӥ$Ewg?Kl Yoo/K)uhA5mn3Ԃ@~_ះ_QPѫuBvL-441!"`8܈Xؠ A]}r>P]u0DJ#S^hh d94yc9ɧQ@灾tFt_صmr,I'՘ƺ%OiP<=y>su,d9Zu,g=hsu БO#R} D(ÿt4ٵ6 $VXdC)# |/Ү,<=d֩s1YZi% H9=&E1IO))OZJ(((((((VOo)QEQEQE75j^(.ѴU+6.Ӡrs:ƣs^)VÖ1Ȳ9iPnf$tק$Ό+/!f.1?陮37|iII%ƨ%a5]Dq+5xs[.dK&@r0N +V~$yKYFm?QՔhdB ls+~ iƋ i 񇉣ល-xۇy#P3x_n ^HI$֠&ϝW]6MvM.|#{{5J+D#J0>Ox_xl>|1;>"kR}fPi v"sJ/G,.RӮfܒ SA]_%- 6:S6&:rzt~'4=oxYoj[ Mbs 4bĩ`hiz5u7dWC-<m7A;A^[Ze}[-KOu Za#Ԃ{׏Ku ᮳*ǻ'/Gr߉`fYn#%}0ےk7L>!GP^$v_M'Ԥ:@`h>)Ry`Xq>4|l3XIcrFT8`AzA:W|/'O.֭-IX׹@IEQEQEQEQEQEQEQEE2tS((((˃:=g# B8dWIt 8+.֔d㰾%^/}O:L(T) 6F?x2O~+YxᏉo%[K<9n| } _5H'$Ɵɨ`hf.wjRvZ4?_Yje͜sE&%M(>2M'Cx#ѡmS;!pdWQEyf+t S]ݦlc1m:.Wkkφ#}CҼ;U̴$mś?Z(?IG v1X["H@b1L3^u iזv2iKe@Pz)W{,ޟYZjXı;.|߱ܞ [EAo׀<c#6.#L(?~Ϳ ~7Z3Li";cM/f |/;FO [H,HE$^Ey>%<+kWKF f%/o+᷄/cbЭ,P6vkn#$kK⟃. kwSbYnz bCg{j޿e.Hmt㶒?3Yi/>944p}ḛ'd`{T 9߄.{ E$z2Nz-(*}7Koص%#^0wd#;WڸFhF}#fcy=ڭh/#oD 2|'g5q6o{q,>HڊT^y&lt b-u] ïi6FKX\8&B1ȯQ&մկuUmWثۡ2 9诔lkx߆u#Mu\^[__Vx {vۈBo05GFm'~uUWҋ8$3,a` IĀee޾t~;PV^V?F?nTHӣ\Ml1!$c y _6OX]=oMzbi[Αmʥ@#=^5Bx9]6=AUg ~lG=+tOڷֱ#\W<[KYu)Hy~F3QrشWʉPx4mkOxEjCĉ ʳ*`vsqQxs7|+Yx@u/>"-'4o/ h _j?-/4OsWza%da}FB J&5t;oJլn\Ԏc#(C.%y p)&+ 91^{z=-; l 2H(܌g0'_ ij]j2Jx>d.ŠNPWv>lZE5Ynq1 7;Wqkϊ//Q&kq%εq$VДR333{_bȅqR~*k񦱪Qx/Y 'yg+o;^Z.D["If`$m<TWȞU?jw i~he*E DQO-U\9I$/QP?ֳФ߁u)[~z '}Ú{i/k.fķsEeemɹFfɑUUqSgqڥ핵r-a+KipP+,3GVR#{u_V_6:ߵΎE;O;~n.;+Oß\嗃DՠƔڻt:!DE(.HG2oڏ6IG|!_?3i/JWQJB嶜U?k]xMN|'ikz;i|$=(jdԱ ($Q!uRrpk/!x? xRM+|pQv|vĈHe# iٯ5ڛſ ~o6G.g[xFHI鎛X9wx"_M ~Ь~P1}{FmI &V'\k:xS^Ywo7'C"H%2eO2|/{#q`pA@s>-pk叉|#6?ml[0y%`ݕG#|ke:Hϸ28-7ixĺ|5Su-Dk:gEinUY>d皳~վ:x Zq-oI&Q i$K|D{nLi'-p_%CMUIfyU6Fpp8Ƞ~{tPEPEPEP 1ѻ{3ZImsiywi)qiyDD ~Q5u쭮 r }MRG:JŽDN\d;zS~w?u __xR6 /aVCqj>oٟViZ_jI_$7AHeFHV߲ uoΰ"5f[+nۜ׽Q@|J«- WVRarFy7ß#AMvyuk` )vޞFztŠĿ(Y _I[H".2! R1|i=xa fskQ[׈㼼:v6U'oNIךExA<'Zq3ipH߅l?xn}UE|[k>"ISDtm6='LP9bC08LkY|= oMib i"2TE`A#ҽvFZ%Uޱjuu%G}f𪁋({IOٞ|?cZ'e1Ia']@ckhnfOun[6e6vј-3BP@li;-ҴG/<>%76d)H/EDZ $澈>b?d]rT>x~ګ~:j5dΚnKdBeT @8|<7_6+h麺蚦V᱔pC_^KFa+'Sκ>^0˒rhp3}gE~|nhxj/%.LAI.eXijG}9\>YBkk$Vi^8֦5B\]7MM ̸4(ۜ($ >>FDI{%u-Y$FRS p8ȯH>cd=_-ƇkjJ[Xw1qTnm:Ꮝ·E/-車7wI,%IvJVjoW}ǯ<:N}ijˍ͆g|!}ĐAr9o :փw.O_ \J ;iDG]9<}E|g'RE'r]hzJ]ym@ u f'n=\fbx౻yfi+(>|+g?n+ai%@lR36KH%ʚ+i/T=xxa 8 645 1063 2 2 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((T" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?p3ɨZq*A\ʜDH5o^8t1V?gcӃ9+}K;'NWG+*!C*&Xer>s< ugzK]Dls4oO~ZUUH# ] >)tpLèEw#qY\8FIY  wy"kkcoS1mL w1d><>{{%Ս&4&@ުGZP3poqkSI2rz|Ƽg歭ZLE 5En7QxU#w,[A3h`C c9pM&k|9VMHdkl#$: +jt6l~T\,w{_xxV5_ }i-᷸TNbASsǯlh?_kݵ)$nbWf2?E Nrq~{ו7Z~u]RŬmHy LM27K}:_qoAL)!0ދVӯiw1s^;06 F<Sj9=8K7u]<3&[_Me$ ɱ?#pQ {{יzy+16.@$@q۽w`ʬ:0 ?^=h=jGE3F3Fyy\,]=h=jGE3F3Fyy\,]=h=jGE3F3Fyy\,]=h=jGE3F3Fyy\,]=h=jGE3F3Fyy\,]=h=jGE3F'R=Oʮ0QVFs1; gmT3`/b oG٠4ɺ1-BaQjzU ZfxG|x? ^k|n:e3#,NI+ӭMo WQx3 W9 }G14X.p?A*w 9 Vce725G٠14/4m&2Povg OPYxsIfL%RS8>8~ U|1*EGBK;fyg>SǐGj,.-Dwl̗#ܻIF) W&8k ozvas~}?p[q.YD?S]UB 4?揳A.ey֏dt}s-Ԟ}*aCކ4GfӴKiC as0xBAGq~mtM{Ofi1؈n ³Cm\>:uWChtMMd\e'h# WIZ^5SKoNcF:+S/|_]-Ƨ%2$)N)-٧u+-HkTLܫ2#떻,|KmuڏК}?(@cӓ[>K7yY.0*8SV?phr\ͧ2N,۾ʦqs=$Zޗ|A ek6(2N:o6"Oj14^KJK91ϿN]]WE˓ 0\ұnΒ7PHqǷ4%;O-V{JzuX}"೻ku&6yڬK$hHڌk ^i^h\y.GqN026]?Tס^jũ%Gڬ;w{U=oڦ1.{#Ku6<1 1º r2bk۵2$)T`28ԭ'Pu{2i1$GZ@hG{6ᴿL)-BH J)?ǥb[5{bS6wYJyfq$Z6£AIn&$ :A? xfPܨE1PqGPqZG}_]V[hŅk {=N9oQvچmh,=ݴwڭ ^EtW> `"Y.֎QСh#Iuv.R0I }H."Y2.{K{ "9=zխeСӮ6œآz5Ʃ%~D̶eqdO,|Yjrj6^-*q:8) ҼM[X4+ AcʟYGīK2+'ea,h2~/ƱbGӬ4x幸,(;`sM}/C:uǧ=kxp|J]׎#5m82\'Ԇ8֞^xu;1C aMu=PvC=i )(1hJZ((Q1@%--5p;_j (((((((((((((((((((((((((((((((:l$gbҸZiVwZPñjKeW֐ڌ RKֲ3 8ip+fr9I* rːzfxSRl5M6U[Yo!R6#wBZA㔟<`o-MQI8s"p2aM;K&LO$yH I4[[ΏBZAh^ S?r<#ѮkZ{eQ"^`Ypd*߈|Y>tߴYj.W ǟH\м?OxWLarS~6m]gn1XԀp$$ڨN1h)R5 RM)C| ?7+M>MGRUaTǧBZA7hm+Dki.a2f} '=hWz/4oۦ#CBZAh^ S?rV^ǩjPj.RA2pxy랙'>"҆{),5 :6"C`'O(мodѓH.`)k+Tr#1+B)hXXRr}=k^Qpԍ>A3qZoÝIeƵ(5H6%* ˜*lIųw?C0atn?+|?*%ѵd +)#qrv.K՞mv@1@qs溏&nȸ"~ ˥E>ͦD"v1G3\}:գ]${s dfςNv>Eo?Mo? h#]RWV`{xl$+nczn3U >vy7`ty7`t7^szO w~H.Wݳ}w Fq5)N-D(1w*_qB+ɖtW߀?9.+jtcvO?>3ci *ᔫ\OzԞ?.PJ0A 9g8]NG"8Ld Ikv-eĞrH؅6y'/^h?C}մVFldϼf _߸?p{oD.{/縐1ij'S;\s)Ѩ?{WKPmv ANO{ͯ5p;_j (((((((((((((((((((((((((((((((5$lT?Aֹ]}oiho#78N[ x8di-E(Ey_~z#7p<3\Opd5т=@3In>}{}Km5o*QNxcYIַi#ivbst^{g3zu5t-P)AڈquFy,WaaU_5=>,.WyLBz{jV/'B2 u8yKËKEDhO-! cި)I{Y!6zZqbb=Hi]vGkt,[Vb$(󊭫xe:۫A*>l//<[LfX~d9OZ_@6[[{.-awBDh0* U zWΕƧbg8npz3iϪi,dFHt3+[Zxm-K[Iq(A`Nƴ[RKk짼.۷d(#~ ^`tڷK;d{yVXٸRq[0A;̐O lF7C^g'Hμ&_M`+$mA9EsR)8Ilߌbڍ ʲ{^0:z>2,_q1sW|i=b]/te=WO[0bC~s62q2OsBCeE,QOaU4{/"6@nڠf67V<{7)kr- _ۿ1gw^jK;ymobXÕu<_*} )P$?OOCTyahCN=qsi03|A?A7LG h t[6|Imt4{KI`.dienc!.%x^=[h]vo-empz8kv5Fչ;?FW'ζqɟLT=F($d $LpT3CJi=^$K]$dg*I[M3mUw3aZKe3=7WIv5ْ| cg$zRcGom2\ZvV(dU > 2hɨw:}憎'*?s@/.&͝ӇF%b?)w:}憎M_qĪTPQQouF]QtKEE__]u}EzyLԕ]Qtou@;u}EѾ__]KLf ~7}憎7: {c)rj?s@/.%s@/.? |bo#@fv]Qtou@sIQouF]Qt5G,07}憎7: JJ}憎7: ij?s@/.40cv:}憎QouF]Qt-%G__]IPxufhk} \5Nxi(((((((((((((((((((((((((((((((ʻiRbco~nt3gk}k7ŊI=wb?.ϧJLH-S/٠K.m5f7[$#j0GcKWռ;;HxcI\AFBh?zתzhךl,FbwEG NN=啝[X2J 64t֮-/7-Ntݽb0TgA&?MFh5kKMKk+:x$so.ٝctf" [p}"\7~x.+X"tb+ȐrJ=1R4"*OHD#In\`dS@jY^n?rѩi.Gv`?e1V?iKA=39@V?iKA=39@V?iKA=39@V?iKA=39@V?iKA=39@V?iKA=39@V?iKA=39@V?iKA=39@V?iKA=39@V?iKA=39@V?iKA=39@V?iKA=39@V?iKA=39@V?iKA=39@V?iKA=39@V?iKA=39@OXlAgiKA=39K/%V&RճT׏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@Q@W3 {Ia m!Fkv-cK7hRFT@ >pO1]*⹝36>F/0 ;jf֥C~ rJx{P±K)2! ~"oG"=Z 丆&8we7YLH6㎼s wM_[6{sG%R&ʠ<~#77zu6c%Ȁh<_Ln Ѐ{ (Nv\[e*d q@ :{]5촋nXc Yw} kψWytm)RXgʙ7? 9(<>3\_&3 M>iGLt .eh_GKh"xŭj>)DO)ag0wBgqb fϿkGOmzVPp]2FIڸ# 5=&+sLύ9SP?uTi??*0=T?Ug|n*/zʌAU3>7_Gs ~UCLύ9QP?uTi??*0=T?Ug|n*/zʌAU3>7_Gs ~UCLύ9QP?uTi??*0=T?Ug|n*/zʌAU3>7_Gs ~UCLύ9QP?uTi??*0=T?Ug|n*/zʌAU3>7_Gs 򪚿˧N}EE;wo$f2>ujm5lK_f ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (3[w _z~(~_z?y/IJg?ףޏ@ ~_zz?G?ש1LP?y/^Ҝ-@_'ףz?YꚥW3zdy*K .\Ŀޏщת:  GNC`sz?F%^d_0\,?M:  EK/_zq4d_0\,?M 1/b_GNCq4X.\Ŀޏщת:  GNC`sz?F%^d_0\,?M:  EK/_zq4d_0\,?M 1/V QN5-6KWKigH_t"7MjWX ^z?R3Ig?ףޏFh^z?O~_z?y/?4fGњg?ףޏFh^z?O%|WR\>* 4-̅A4UoyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyhSyh2_ɠ@ (.؂=pkofh,$Qʞ3Ck X0A;%|7l4"r(ܧ>Ɨq8}C!G &+& zttO]wL-8E+TQrsG]'MW4w"0 F:!_jО;6Ml]"Wjԁ~h-ivZuͧ{8.̙vV AĝWZs.s'چ;n\z'M3O%TBQxci5wq&dOUX~>bH/%uKd$Iʀ<`ڴo"kvseGuUB[kkB?bdy+qq7&?m6ْm?rO]00;j6'v>v'āQ|Q*|>[anlҬ'Ue@vg@`ӭas(n=O H{,kc$A~>CA->YA&%[k*ހלhw]pȳM6лwf>~? +ͯ*r)T/c觘 7 =OHMQ2+-${h$vV0{O*-[?QTEڔr2:z7u\A5:|2dxQCYXXt@I=pqI^3jmդzUF+abS K3Au躏n=B<_z`n!Id+#27 uŽEqq R"GpC?JBf&hEKڥk>um^Y"BA}hJnSgW|W>˹tQHd ф8ۥs&mCa=ͭmb}c+NGQ:(3F/??´ ^ϬX Ӣ31{>zGzV}Sy>իEelTj6^Ϫj@[/??ZZPVO֣eV}Sy>իEelTj6^Ϫj@[/??ZH^x#^$֪z'(ҠףDB1#Ҭ[CR^~k{Kpb?WRiBF њYjz/mr{?ƣ]>U9?ƶFindk0PHń-[Y4YO҈.H\G*̑CiUM}5k9&lgˊ6 CjQʃoy<Pj:lwkyB Y۵w?Gݯ9C|= izr۬l>Mh/XQk<"P]#}xdhKG"`XrRm;ny Ech%]/LX%q~ǦX+XxO17?Ɵk<"_3r#=?>uuSPEQ7n=sK9]}_Cj ǪZJ\OMQ/]#} >u<*?(Їy_ER5? ?ԟ]Їy_ E>;du@]ef9CCY"J7OQ(V*"8:gEln1w r}ր%2}/&?y1w 0۵`i>SFy=bI._-OPҶ|ր$dP%HܰWRր2t_ iF%S3[]QV!='gVEf0Jb-h9֌kUO ?ߵ3Zx~F>4c3Z??ߵ}h?#@??ߵ3Z,Ϋ*$3hXL~֏?j,?#G,?#@??ߵ3Z???hL~֫4}4c3Z??ߵhhXL~֏?j,?#G,?#@??ߵ3Z???hL~֫4}4c3Z??ߵhhXL~֏?j,?#G,?#@??ߵ3Z???hL~֫4}4c3ZFYJOpd@3P}4? ֿԷq~ (qS^,8`e=-UK֬~QdFF2T>9П~aa$VpJ 05xsĺ=ef^52aN$quHq- ~I{| Yݜ~^n嵷么$K(,uҸڮ7tӬH_>^HU 7ҋi-aOI/##:`zK-VyBIA8^2/EX382+vdD3صF9 XÍ^HbɧC䫅uVSBG\/`z`zKm[9Hd}GV>´&+iRXe0i&Ú~<63b;M$,qzרZCB.>\R\ {(- p)* Ŵ,.vc%N6[L6$k=WR:UN{721vOb.|@o(5RJ㸜e(H8lxFZn@0zIxMT[qgP3+gfX@+Jx[5KEaY\]Z298 .-Z^ xzF+YhNA kʹڎawiq$+zVj$==HaIU9?w7szv6nszvF@ ޴:yѼr@AˊTt+Yg5zoxO "eB8GH%cXWG "Anۦo,0:$N O^D{KV+!Z'^ "|Ay,W{[-dc+yB:{R  ж37"[xj+QHyܒƫai:n Kyu&h~ TI-7p9ל';An[JD/Nvg=zЀGĺ.5:gm-%UR"7t8uͬY%"Zay5%m_t[. :ᮕBc/$yu8Ḳig,E !ӂv#a9EC;UO̶.FOC?4/^mj.mL+p~ yj:jPj,eP`:u~]܍:qjIn$DzNhI]L_ zMfk Ռq\!'i$Qԏy Tpwb.-5y$n #Igwi6 ;˶F͒@$H,Ou+sM'Mڄ?hp;0kNz=&eZ}G ,1׏j4߇ΘM.H{KVlR$1*߅|3}Hs5泜Ì!y腛noSNF@oSF4MӶ6n4noSNh@~&_7zpwqFG*sGp)<@Oꃹ RcB11Sk6Z ԵU$ӚɞYm4r `.=I4uKD}ai@cǽfh;еiu$8gU)qpuEY9md6wO,Da$Vu]Yxn,!.D dk ҕ#Y]. ȁZ' n,vY_ S{B|;F2K1Z?On{}@4!|Woi_|ΰgaIxE$^Y E90=#6ZI`Hn˅mA=wmj3(H.֒zj3izm[YT8=z4j~:,t[Munǚ+8$9&Of ťQZC(Y$OЩa${_DnYӖIJJU: &$z1~6RqCp?v^eqtz.񲺋 09irэy}[RO2ix^%8X:ONFSv֟ttmIJ{SEi() p-gu8e?yV#VHӄrjWSMI'!H\t]]e\<#_H=;jn<"s^[=h[ 2;#icf[n8I~&,d2^Oo i h3}Ӹ z][xKBdV[ed;^^xJKMbVisF~"Oke=]iyKq]x@k7@MFߍ|;++RX[{ tzeFi'vG]k4=GX˹WK9'=2,um`]PwgnLʤZ#˩iJ{u=-lD~ϷKhw2=nWH F zb>igXDM,O3e*zQ:v.kI3,sz>.t3r{o|r^ΟjͲ2Y!VrakKHӾn(V/)tQ݄q6;'k䰸ɍJӞm3J-y:vLP,c1 co9#Z! .umf69FU[Xu hWVMF.Y6x*J$C]\:uid ]3tdcҙj =2_:MQVF9svz򠴒5[|?OׯEx/}}I2'by$s:n9EiE-#-0#*6=NN}E$]AjsV:Zw(&6xwIQs]^"aQ..>;w^| w?1cx{7]vRE~a!*C]u6:$p+#tFX_ [Kc*0X6''5RK6m+TpAglI8ƃ#GrBtn5}Z%DȂAڛNtiEgo}o\ +?ϓţϓŦɿɿɿɿҸVۧ&Gۧ&L +?ϓţϓŠ +?ϓţϓŠ +?ϓţϓŠ +?ϓţϓŠ +?ϓţϓŠ +?ϓţϓŠ i??7Zynx^٣8,[CR^v>2|g _[(a`1k3[=`I7H%zsJ.mJ:W/pxkP`ko/[2`9r8%x=)4o qO$j<,wtz v=Ni+ZwבZ U 4pLz4lla"S:x\|CxzCo%M?t#mMAVŜTt6Ol ?7NY.la&%s$EW^zv GS/;FOl I( ѿS/;RQ{?7oԙ4Ol #hH%Q أ~\њl10Q"Nm1N)Jm-We)ev%sCC)O|ԛ?7&hGS/;FOl I3@ѿS/;Rf{?7oԙ4Ol ?7&hGS/;FOl I3@ѿS/;Rf{?7oԙ4Ol ?7&hW#m },$q֬L@ =7ʗaQ hO7㴿g?њ}Sl? 'O7㵡3@fڢhUг#}<0CwϮy4}WfK6@? (>?v3FhO7}T;WFh?k?ӣy{Օbm0HH=}@٢-Z qE-{ (#IE,GҸ Eֵ it(#HTd"1g],y+LW? Ə /mX淽tE'ARco᭵kv[x`Hg5|kDZ7T`1v# ([x|(hga/2E$c;8-[Ưjͫ]ixxЮͤ67N6ݽjy7*c!cU5ZƕgPAB)''YWlg7C4f'w9lxxZM\Y(dʊmt_ e~j2K-vNx2}3Z7'nخ(D t+}^\u7E%F6hNq<9qo)cP.:ץXKizLM5_dcjug&kas+:4Yd Z@Aq@;o/5쉣OӬn)b*rVx–CkHS"W4 wGĺSͩiic\g9nx[I+_Cg1#)=n:]Wҵ,5 dwP񛉉pH k'^:26"hYW#;KskʼqLuKOdT62m m r9Ì7}v\$2=s$P젆Ie{5,2*Ǐ|c䩹1ʡwsN<`0M1>j ,ײK)`} EË{xU{k; HH*5n/xX<4ZIh t<78sۚ{BWshrn%#$`Hd ~?s,'sڏË[JY59_j khʺoX{cyY kV. 09@tQ`_kzM~a4u$ ,ĸ9I-i-i5ܱGӖafqY=Mo2ڋtPcf>6\8+ek~W(8/O_G^>6€qSek~W(P+qG?^>6?|m8/O_G^>6‹8/O_G^>6€qSek~W(Q`qSek~W(Q`qSek~W(Qa\8+ek~W(ATzߕ ?+X.ATzߕ ?+ QOύ_/O_Er(Qύ_r(Qύ_r(Qύ_¹qSek~W(Q`qSek~W(Qa܃8+ek~W(\U5U(R4*T`v /MQeSf+#7w_@)#xU Q|Nf̓(8+׿q~5-_RoEsOͮrqWp7^;oyc K֋ 7p?ZgI-4ZomPT `K+>\W0QN0bm/7ү&k%,ٳĕԗՎ\,_*Jyl ň;t=<9j|Ekyal-%͌p:x{ڦ.q5&\q'?(l{-<{oG.Ägݒ܊׍oPohͤr,ii0\cgh/vz.s95J}\}NTYEFYBg$uS%6˙.%74Owss|:S^RH<$dzRCv_ Kj{/$7Rz͗o&۝j}$,BP6v+MIgw2"pÌjU-t{2{%H?tͅn86i3oR)f6" ۳nW8#KxBL֞Q$#oUq&3Sx[[Ѧf/ocʏԁ9kvU2Dѳ)Tǽ_+!o⼽Y1!bU{::9iPY7  [#ߓ\>;h?7kq}M6k˜*^pTOkvEy#LrrqCO}.>{\Q]b[>L{Dc%Fv栵e6RHu(RXvTP3O'fڝzjYp3,,(w%A%H< 9Mk=>ikGw` 0i 7?m^'[gd0fJ#eAa\ZX#c1Ьnnܙku{8v\|)^3[E 78XȬ,VJɃn%Z鴴ISYnQ%~JcҴjO} qFK1sQG+D"%d⯚G\p KfUgGG#ULHT "6]ݷ<kZŚeΟ$_cNnoeq~v2==N?gyٚ5,n 4@dkiUFʜe8# p;ӮAce$kx|_@N=>WAMn-~k5[Я KxRhƹO$T`Z4ӽzQ%d/ww @iCݾQi5VK G3;f/QY^'zݫYcb2*8ȮG_5}& %_R&K`_3j'-{|.!7 320p늖; |=|&5rv<_)_wƓ4edF )= xv IEùEBQ d8.{1T۽w'>$䲼ͽ[kkrL~S;0x#fp8 S|9w6_PȷiwR qz`z/z*5,)wҨO l2@gܬꬬpT8?6k`13!ځ g1[%x&ʚ$Ȝ`6 c'~.Qq0u{eK[ FI-ߒ8@IUhf\^K2JU((ZoqY !A~SYol3@X t_>.mŻF7Bx?uh⼳ 3XhNociil3?$g uCeԬ"Fr!yJEs^폈'Ah`]ŀ $mq\YZK`%q!_ P 4a|}@ pxV|I oUOOi񨍷gr=r9=Ez희./-}^ZE4qIylDiTx?to~{s F^mH/+緶oD]Zl2ONPIi;$L#\ sō /v7> Ⲃ[ˉYTAoy'ֵp'*U3Ӷvjqَfs>$Ə&I-B>'Ϋ:\Ѽ(΋AuS}?̤2FU|<.;7QQpkyyee 7QQpkyyee 7QQpkyyee 7QQpg5V]FK`i8Ltm?Zi 5 Tb(XԲlohﭮFXV z*γgմ/OxOJnM&/e!5p۵ki(5-_Ek} K{@?,?oYejڼ]귓=@f$Ti7~Xc޼EouOݵ~-?1偎Tڅ; V\oc[,rfyz{;\`KxW̌[r F ʌ`"<-{_ j-_yıv Ysa-k{w)Dl*.ALMUUVmTmaЄz&=w֒=.$=jܚSvL N9N}k/O[Ek#<`Xƫ22A9gÚ/nXdAdj^h hpc+yUW"c`A^7ך6qW$Q1?o]=wctRZ4<] chcq88ǭ>_ 鮌Q lm#FJmUPNw?0;/xy^;Jk5t~XքZIA%\ȣ\Nڎ<tV73`hF-cc!ǂqڤԼ=kwNHb#pL(`M5']X~' ;L0]Ī$pf 2}M)'c,@wh;%\7ڭшz,V-dV%w;Es@iiZ{}h/0om.N:($tmt[ZN%8Qwwe˰ 9v¹rgY-m-J:rj>+F@2\ P&VJ yLVe$4 }?9A>C+_2?-' S}ڟ_I_h̵ƀ1s@}OQ?r?-'2u?9Gۮ P̵Ə_I_h?(n6C+c2?-'cڟ?(lfZrOGe$4X \S}ڟ_I_h̵Ƌj?r\S㕱k?ٖܓ`1s@mOQj?r?-'2,?ۮ P>s@mOVe$4fZrOEu?9GۮS㎐̵Ə_I_hm>5g6ѤF83,N8u{֟]$5 @21hk_j[߸ZjK߸Za]zN ƿ Lrd7?,?oU<-asI."H ^oOW\6oFum:J%5%뿘b Xc׷nxHx-d^,G>8?EoE̿xmr#UYFGL(ǥ14_ڱZiSZ[5ؑB,p1NGx E@fVb0bN8\<,5A 97H# X۞͞~Ȧ`'M$#SF:oxmnm ˻ȹM'/cX!ѵ,cxwK dEMH-H`]>#K–SItI"H\#d$cSxQmTX*cX|ne]:UwLJl("i\p`@; w -2;Ą>۹^i7" UmqZ2÷G@1\_RhC(F$ێ+Q\ f;þ5ܗ$Oo ˟3yX3c)*[1Ԛz7X ].3(XJ"G;̊B0{U۟Y=O2 WL1)Ÿ-ֻebڴ#|=SQ<$דZK4Tk~So |gy]-~6,7,PZ>wRO֦𕅝WDn CnӹB{㗂Jik`<6>lhY\xt"(E}ܪe7|F\J|9kWi&hy2'Ebʬ;I+ X^5d[s,1\wH 7WZI9QOa=A5=Ni GhʄEFH|=1ڦ=%ƛp.{6O+gnj6-56ȷ)>%ecq;vȩkxQӥVG(d.3cxK_C<!dq(x}jEjͫ C˶L#$G5N_.Q7 srYƳibKQ9͇=kwI"ؔyz{ u1-wb! "g8aFi9?WV ʖhaId`X*K/ڬjZ޷oiAHY\*)^:1F cqbc,^JI91Ĺ`Ǧ*{4u{u,EG] :{S"ƢyǗLڇ2x~\sE`/ [;YF@o|7dFOA7` c4\:\C,wSvcϹkM{as}h  x9NеKBTxsF)ąՀoĚ.n--.Rydx%1ځ#\R,zw)$YjʱVf,98kM𮡧ji [ڱ|?$`1֠ռKqPZʗvqMw۶yZt#K24ogw%pNY@9<Q046sjߺ6y\5} w `Dy]u<=.mOb77 c7`=Ͳ:kXO n<|4cJҹ|] m#.:W(ukj큏NBRD\/=Y~9"Hͮ-di% -ofL9#) 0ACiYpffgf ?U? [WktB\PBe,1N8(JM΢X[Y#*pABO\ GSdmYcRnDD_Üw\S -%3n30nN8ixsPM>5x[9?#>` tEڝ4f6znkYsē Oݪ$䓎foKVumj4OXvսJZ8unPU;9GkkX.3"sȮzt'S mc |ϯz,#t@sQ][I0BA⼯—$M}y(L+owH@N3I>zqOc%ı޹kkM9O3;< ԻveoЎ 68HDž!@'sg8()3LYQ1O0\ mEۙ'KI$]vܸ;Թ4]ُnX&y#, XUploo5L1;v9*8=w?z-3^6HaXv 9sʀ=8: TWPH.&`3ǭ6hkk1zVxEᲊ3ٷ9zhWjwz]jV+]bcx7r&(ł֫s -$%vFq@v@.x;3k\xn5+9׊ ml;YNW ӎsUFG<^՚Z`xA[F9oxJ ϔHyz5[oNG̖k`!m A 65x[Kv5 sTKa\[H%DLb2QY@5׋G[M5R,l v& 8kR v9\L8=vHE1^[ow Mg;~5xo eb[zHz׬PiX/-a [PKOhv'jZGs5z1'`>Un볩R]|w zti4QeeU-G#5O41"K9 #1LE<;GLдe6ݬ] s}V+̯i$'fؼp= fmC,ry+|pW%Yu{yo-$' ҫq9ӽv666og C B I59=֯!k?`#n~4z<57;1S z:fC.ʐGl)Bۏ\}EAsiotb7Sp@QL Q@(((((((((Ed7oOVLhe,7QLF^ Xik'I"iˢ&DB1o |[OS5Yucw, uHgQ>5/3}P߂1X^$>sgai?ie f1J3dikN]7?ly/MFarH%F`LtOvSh5qDO!78U2N?h:%iJ {iz/vl(o(nL jh:$Wz}3d ,pHtn1,,幖(d2fE>:+k ` *לXhZnmo fEqrۤ\dd=1SGBMoiXBQgc6iZi&f{{U,BKg$c!?x5I בE ,ȑoRwdۭfcI 6Ki?J@?G u+O% ƻMlVO]oϔ`dJEcWs>fTѧhZ]2/1 '$|ӛ@ZN7yc'#EbK(hm[!I^p{?ַ!8bPP:KEB-\H.43UѴ岁h <5~ЅZ &[#XC N2G?*Fv|'mS`U( xkDS9].l 9@5׆ɴ[k$'1@*n:d +niԫA#֨ˢZm%Aa@օxwGQRLUJ8IC}.t>NI$5Eg[V椅$o3ZqiopдFXgc=ҪB:Y1FeYaT䣥3ƶ9Thwml9hmFIRFq 16oQEgMuP%#K12[9+z'FӯHmV#FH 89 t闑Mk:TPUf, `w<X Ħ7ӗNn.+xb^LKJ[/[ 9߷ں$9X +<;iZJ1!i#^^yXhqjbhO,dc@msTFӤYZ4*w1%~H4ko+$23DP'~`0NkC]WY4gGܼʰZ7s\5۫K3UI,ul=y4=ψ4;M:{OeFH.wyGeTFxN)+yђG"x.;k'±\%T 'q`Juw{%ۻ8=|5ޕy غbaNxZk0jr"x&qr$AXtHrXab8=I/4[Mdf'ZJJ34q@z'*R?1@ԗq~ԗq~QoOZ?tSֹ!((((((((((((((((((((((((((((QY !A+!#{"/o/MS*?k%ݧ|plXي)U>Sru?&jsaR[ xeKO 7HgAa=3PTh uo3y:}jY{Y]/VmL?y)=}k׶Ovmb*KBqקz#񬬃JE!Xs}9S#!J:|q-"j"~RFz<[ F~v,P‘3hw729 v4]KZ$[9"ͻ%NuVD$O[THc.^F1B 5V3ϕnݓ#}+7ԚXwY@!ÄVHryֺ"C q#3fB[]Vm2fOnJm ji_;ve$fgg`y`p0OI=oq\4WϋyD`ҺtaXk8 T qqTKSX" J9twP^ZŤ4 r5&>VkKՎa [2Md*Pdg8=ӮM/I[(lm v.I늂]FѼSic.$^a?u /ݶ5k(0<7vڟ½JI^"'VRF#' w5D$ D :՘lm!DNV0}Gz]? h(-Rhk/){ NOlݸͧ5-(M"=Ѹ2eFHs~5]WG[Oy˂bM:Ί.4ջD/(;7nsM%o,: 6{eS]*mL T%`dUHI1m=+MOD5Ŗ $e60pAGҬiv-)%DpQb&q2z jݹ0>c|35Ck#kR'{y0sҟ:`d#-40@3שi2)VPF<)0Zlg<'q;B/m3qa<;dS^#WY!UТ6xy8-ۏt-4KB(%I]a9$70?Jo6(兜s2Y Ҁ~!jW|oEXmY؅`#,_>^N j[kzŕGzQ-?cf90ӊ!DrfӶ"mrU'ΒV$6;w4[1aj0 .x#h0$@yLE28;㶙NqߝGmak{uM]?1r7'JӡXF3$px] YcM2(θEz%W2]( yWPGo>Z`$)R@rږ{xSDK}$]@rbt$|%?2\~nDl 7sWmeyIs=*#āƀv'ޥ-2X*zSgwy]$tUx_8\: J[Zçw>7+c8^3ˏ~q5uc Z|Z,|6su#!cBaTqꚑM1J`E1. /\guqJ- 7ވ2JpIZXkFQ(m@Wu&`qa⛩ry$Y] :b?>%գHD@ @'5E`xJ{oAHcx'y&-1^XVuנim`_]pZT63o(<,6MrBdu~ծllCm[\M|a2p8GS8^E#IKn6NT_t%]j\l_ڰu=T}rv-<(RaHF)x/Z4T]Apa:9ϯ:wlqshjW4ˌ&zgf;BH$GLós:ڱy`(<`$D֥% q:EnjPM]߽Ӷ-0۱݌d`<D (?C VFb3"1>WqKfZ6{d֎V$$$4VH;[帔>0#(evJvsڀ&"~[uv6~+@]ZC$t@hz2Y 1qق9SYK$pK4~TG$d¥◵%Q@Q@Q@Q@z'*z'(_/֣_/֐-v?)\C]z4%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@ +!#{"d7oORcE/MQeb2_^MnV H)QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE 2b 2b o/~j;o/~i cd7~觭sLBQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEB7'+\VCFE&4_TAE_(#/UUpa ܤJ(Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@z'*z'(_/֣_/֐-v?)\C]z4%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@ +!#{"d7oORcE/MQeb2_^MnV Hb E.(%RPQK1@ E.(%RPQK1@ E.(%RPQK1@ E.(%RPQK1@ E.(%RP 2bk? ?jK߸ZjK߸ZC(~觭sY v?)^b\QJ)qF((b\QJ)qF((b\QJ)qF((b\QJ)qF((b\QJ)qF((b\QJ)qF((b\QJ)qF((VCFEkCFE&4_TAE_("񉠖'OЌS<}}T567 4T~1hhEMMT~}o7ʏ4yQ~[_Ə_ 4*?OGw/m>k|7|?SGhhhEMMT~}o7ʏ4yQ~[_Ə_ 4*?OGw/m>k|7|?SGhhhEMM/EG/m>ο?O[_Ə_ 4}?ΟMgO[_Ə_ 4y >ΟMhhEG>k|7[_ƓOO!CS@ [_Ə_ 4}3GMhhEG?>k|7[_Ɛ%?__ 4}o>οMhhEGMBtf_ 4^6ssgOuSx/֡P55_Rp|dPbI~R5I"1j<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M3m:<}!ן?M7?ޓnHhuCMy>y>|?:imGk7[$X@bABz7}"hG[8Z6zwͼi-"1݁kz׈u>4I$k+"Ǐ@}i#ٿ4M8Zok^$[+[=+J P-톩i"<6䓜A}7B:nF-#LאA&Z [P! ?ZU=Trff8b\ɷ>[֚/swnbqq8^}/L,y!s1ӵ>n[ccTnI@Ǧi&#M8Z?ǜYku_ZW[ ( %@`F9>b 5FOlkٽڥ038@6C KmD]jfˢǪh)$LvȻߵyNuCsuŴ##EmP`59mip\LʬJ¤[}_!)i(Z)(Z)(Z)(Z)(Z)(Z)(Z)(Z)(Z)(Z)(0۫yH+Xi-1$z0VAFWs &>(_ozO&ۧ{Ry&ryUT[ciն6qp>ssm1֤hqtT`}:U'?AEOi?4t?٤gTfΏI@QSO:>'?AEOi?4t?٤gTfΏI@QSO:>'?AEOi?4t?٤gTfΏI@QSO:>'?AEOi?4t?٤gTfΏI@QSO:>'?AEOi?4t.~ytf;,HimH!rz*[҈% t5}<!.Td~JƏ^W7 +iX XAv>Tøal an@&qHu=)K  V7G7@\Kc7OkV!B`Ϗ|?: 288 307 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?#zEicjF7;w$pe' *GL~e"KYu J{>ޕɉ:m_VCUwGs ⑮1TGP+ξj$UvqzWg-Efdp]'6i%³);Y@4=«m UwښXQʕ]@R =x`-ѵJ*_#Z籺*X+Fcc: oL漊$Gء*c@k :Qp{x_SLDd$#p^2G+>" ]5թdl9r;++Z?/ӵ/O46uc ns|ƕ{7=y!^SQLӮje[/! :>{OFl!q^N&Vf*BzӸzG=y!^Qksɤjؽ$FY#)NIqKOzα:r6q#) 3~SӵUB0z/+ʼ;mnRPŴF[HJDdI85NzqXE`_U/3ޏ3ދ`_QER==XE`_U/3ޏ3ދ`_QER==XE`_U/3ޏ3ދ`_QER==XE`_U/3ޏ3ދ`_QER==XE`_U/3ޏ3ދk ƶD6H:~TWCq'=.^V`c \..[[ƷV9(WpJ!mo)RxXԡW:hjPws:m۴p1yӍnߍnxIk⟕\i+Gc9u$-[1"ʰi1)&1a-Oʏɹxˏ>pO-Oʗ+ Z[F٥(LQ؀@=jft:J-U]-MvDzֺ-OʎP8 7p-oٝNHօX]X< m{z ~oooooP[Y7"%m*W׼[4-Vk̄cEqE`kJufr fa6ƭ9ӊ["}9S=ģk$`(ZQ[ֵ⅚5 $i>ϭXW:{ゲ5GTӼIżxߜcaygֿ箝~ӿUÚLP7߷⩫ӾSm⣈~(+ӿ>=t Q{94rCy-,g#{ÚzKf#mo,g #0A|;?N>=tUj? |Eǡ[4Z309 tϳ_NI]B()HǦAֹ~1[--u mV[i^IjV@We[c]CA Ү-" R'D ր .m$9d6zU_ijzwJ2}Q@ͧMwWsṞYM=AxfKi渚h$ҜA[(?99r-'I.IeWU>i:š{pr敲*55 4$|гʭ1t<>~M m&RÌihzV9CòC! );jZW^+ظP@Ml\fFf"0Y.sw~.m#X??@$U. 6{|T>?@-|?EX lceXݍ T-s=@GmsU-Ճݢ!9b{PE!EPEPEPEPEPEPEPkM~?Ǣ/`l8kšSjSoZ^$;G6CџҶgpn2($DwFzj75<7y1|~u|3[ZKg%g`؆\) ӎh`uP nq=kM㽁RP79_>֥linZ .i$a&#UR6#'nsocGz;1\4__?VzvJ U>'Wzn2R޹'$O=2~C+iI[RZa?֕5|BtG1Rf۱ s&1^Q՟VF(ZrB[oG-OmqH^W; 弉sǨi&-aKid uSWU ? _umm3Zf[]Mw!JcNxj6w:R^t^vؼ@\/`O4/0gWQDO?A>ڎ%P_Z!I;^@i0)6G(VVwiW,$LJh:f h0#nupɠ nO aPGd`/U-oB5cW(z,f3xF敤_;sӽ^%DC\YxgN14vH:v_\ێjCEC(((((((j;<=댞i4_Ngh/m]PH|Fr8 _bJZ+$$x3U)dh6_ZrPF9"i]r ;W[Y USծyۄT˒2I l0aM1y=lU4q0?G@?@}z SYϝg?tߕ dzd2φjwo*[?G]_Ge_ku˟o*g|7P> ٖx9KOg~W(O^[?c2D3#$m$kOlje^/4+휲*OQ/]94BF o#z4Ph1(\QJ)qF((b\QJZ1Ea&fI |t}kNmgP{ Hḿv?xv4ôz뷐{"6PHb9'_[OkȣWJ?&(!{j8JM>25 ]^}77mPY NxsLԴ&MWYV)$$[:az(?{hgY"uF$ҵt4VQC4l-3ܚJ?&_[ɞDD%Y~ ׮(sEC( ~44vOy4vOԔP~hݓ5%?'hGd IEG|!žK?q&Wx_hǝ|Vn9=9VD+0‹Ch{RKY[M/I>S~`ZKS5K {ۜs5_y_am҅ I*OFH;X21y7{5՛PLqWml}k\4&݊cػ dWeDgv 2K(D5'WU p67ٚn-] d'9t-"ng7g3lQE wZm Ycs%A8 OX:=a8nϦEL5aC')[Mp͹_,jT hag>(Ԭmⵚ;w)95"ZfTĀ#`˯ Oͪnx-ܳ܏sVmN+>6kiqq)I@ F R5 Y er֙kGwa'@ir7"*ɨq{H!Ji J(W5/Ko%-qmf33V:cFku%*nGC?tw[<o߯##~ P+EX*UWyt>L0YQggfcԖbI?SV6\tfE;4PJ(fqYI$U8@sU- xWH5cR!2dW164-%2hi$Qfc@i]őB<?:.-^"60{#MBQGHdp;UPv3ZMQzWWܴ)>}J}ô; sWoWo??o|@˷Ic~w9S@Y 6}wԟh_pZZm>߲Ӧ>E@1?wދ˷KjGگi}U;Dy쭣%.¨$>yk ͵LHG dQyv; `<1tORIשg=znf{pn%rA㷭hNQKa(dRQK1@ E.(%RPQK1@gpmC11ۻWME[KeK 78b%ce1*8ēV֯UX[%ݽ0x 9Aڥ.:o>K.9Hؘ6zUGTEE%=C  koDPƀpjZy^,M5(ֿ' >Qom-Q%H Yz硪0 asm47ٖ<?\ij\v}'mPAǥbˡv+sgeMfhG$쌄r h :}KUZkvgE03[J+ \a1X5ŤM ł5ڢ1!Vo [uj1"#e`2q4&ζ7wonٹn=jH*FA p. /f[w[g鱼vђU^FrybOS@Io5|f}J@2Tߊ)/YxLKy%ԒXHx9S ͎ަY_F#'Un2uORSZUdF c#/xgMxQ8ypv$sҵa-7RQE fu@7_3ϋz(!F}BOB>/Р (B>/РL} <B$yT|_?@=yD>gOQ=Zϋz'(} }>/Уϋz'(JJgOQ=7$r9dRzr3N7/[|B>/Р}?%kkO o7L} <BtȮoH5,O|rOϋz'(} /[t>/Уϋz'(7iZr?z/[<B>/Р}}?%DRy{yjBGHI8)|_?GOP)|_?GOP Q@ Km)P@E:m8PsO %=݂:NKw.e.FHU^"5YDV>dw1n_Q q@ Ӱ_GZH_h*;I,7p;Y5bkMVd҃zIq>{-=7\uZED[!0U%fflry=M3VguF *i_Nk覆Z6v.x'?M5 ),wǠ'5u )2 d)#^9]z i<ľCa?ֿƤJ.]W!WTzwFxᴻ $d3Pa?ֿƏ;5 T7Ӷ.a?OO{Ĉ G M<{$ז6a?3NOk-%bI$n#$FŔX4lǽAa?ֿƏ;5 1?԰C8& 7WKmo,dj]9`!^Ydb0XE6((((:i€)_dyѴ)ekQ Pmuf@nC(#UVhF6H?Zo].EP."K9S$Vmb6`\>yLN>[2(CF$w6}__]je@Lic6"c'w]'A5[7NT ?M]mDUpHF}տ}>51I5^hvK5e@I$ @-1(ȠkHkDd@h>%GriAKNw@m" (mۘ&2G~)-־^ƺl#ż)" ddfo]2]2/ ۵a"#<M#RuSsas׽kqj_\FG`oLJD`B"oCbH5+D[4#@ڶ.+Fe_3`Qȏp݃Kxi J1U︿qusMVŤHncd\&Bc’pqrqZ6vriO$LS<~~RN9]]eAeM?1X^xYgY$ vƻKux5*?)eȣW Tn%Ы flt 4w4%Q@Q@Q@Q@.h њ(&h њ(4f(sFh4f(\f(3Fh4f(3Fh (?fotoxx-15.11.1/data/images/auto-trim.jpg0000644000175000017500000011413312616075370016462 0ustar micomicoJFIFtExifMM*JR(1Zgnome-screenshot 8 415 658 1 2 2 C    ' .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQROC&&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOa" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?))IҜdOm›mZdsZvD&Dw3NA=f ׷)rb'4Me@HҠ#YM*Q\rb]ɼs9R/7v KL?ށ¡%i#p"fx+\c4G>WM;_)5NKY,OܙXc.c?9hw9tV,ڥ_ n;!&-n༌pĢЎ/=\g=;\1W?|"=wR\\k K<y$]Sk{_9!ʪ W+E,xdRy}>sOSAL!G#4y]?xyhxaǛ"&zn`3T#mSɘPAW>ƚb[|_+})Ҍ?+Q).Y8ǯ@1.E__LyZ=3OOδfdPFqs(j؞Y(>/|_/hsT]$ FY {œ7(?Ȫ0ȍ'PZm;Ϲ2h)7sE'??ƞH$T")ǏӚTmv$<ǜ|3E?x41Ryg%G,D7K*݃h*?Ə/4i]<(?ȧG4gE^z??ƛwp8b7&]_PTڊ"iaU-7RQouobr(E_G$@yQT <|<|YςFibcks+>jWu-oR n IЩGNMu_Gyg%6Gع=;n:(?Ȫڒ$q*9cZuӲ)'*}оGch"(x"5dQOh_GySgEY:)ҎS ^.Z?<խ1m_jky #apF>P>v2rG>e'dn0 g~nyOyW?$` z6qsGOtZYdF(oP0@3ך `ӳ\eVU;GaVPŽY$(^f8-PZǝr_* bSKotֿo\ dxoZEvE`v! %/cړ񠣖mHJKT}t~Vhm$2~e$~Eq1OzeCclYbX(< q[Ư,lbIEZDC$/p@OSIX\hs~/ɭmjɒ[q5y7_jfo9xݑץ:zW^ @C*XT;M('fQOl4c9z:[PVvd+!3ҴMac- #Eo[2,`HZSsx'26F~eNP?aiX6a ԟ۠$rTliQE%bjPȷyX "nS[|hxSNj>pW):5Ғr=k>]&)x\@L/M2K+fI+h?$rRn(X"27aTecv(Bųc 8T/ik>z**.,,NvVhTP0tV&-cܫ~+ $lR399z[hn Jrw*AY\Z;,s\DO!?Q( #?A_o^dRV?cXj*T"JO@{͆dA& #!4S~󧷇t<15$PXUG ~Ћj|䲐O?*zmm㶷ˊ1N>%!\osBep8I,2Oӧ]Pt^iٝ7q;zRz.?[i% R̍=H~X/N@:> mZ^2W9< =:63eli*PQ} QEQW?6;[߳2lUxB*ƍ]NOn|\,8˕[YlDG;^(y9G_Ls]snPP23@> e=GjMB` 9#jRsY*ʺ_컢9f)5M`ؤlV ʅ瓌~\C- * Xo)%- csc?i\>y-̦I%ۓ«r@+=k/IЭdIe|1V֐Ɵ? VXS$=%CT4fXEFAl=o~au*diJjGh_L %KU@$s=;՟uN0Z$f[e!Ku91L7vcϵ\м=&| un>`sF-n3ZZIvU;8<G'V$_a|bK{eFÔl)pIAwZ[I/~^B/ tPe Zk-vG[u"%R pӜs. YLhW1r[<av;5n,n ebÜH/mȒ\dS_n=Y ki UtIZ@_OY0̻LU28%=I1b|mČG\ɬ]FR;kTQCG63hUu#+jG$P*B.-2Ĵ,qk4eON_\qzqcȿKIL);JV-W`+G;%m串mOڭJ yk 13ޠfzLgڌ, u=XwQ-ͬVT(HA [/}=5L9\$Ɍ:=`EJr#35a `UZNu~Pn`>"Ii'b@FF23^h'׋)l-e@d,@W3@8##88 $gt϶ D NX %[^^YFݾG'z gNҧUMB@cE0 Xc3w)[`*zH_N\`XnI3A sSױU4]Ʋ 7Qе>Zs_j^$gf+f $l n$zCC0VOgUʜF(ĺWvr +h p}jP]"}JZ&vxWI&F>{[^Y,8v6 b-%XV+P#w\TF ˥\jO<0+Qs)!У$7Ro q dl9H;`˼qoA#Pg=9#PAir:'ȏXr;ԑx~4E%ĐE l!W8ԒPm<yk Ҏx%DhpXҪWi7»B}*(8CeP }}jHo洸2lu Zu4lGh Weӝӯ[(wkt%RdbT8ni_jEY%XnK*H-J AR]fCsoq=Ωh# 0I5eu WVZ9+;lz(Zhny[$;錁p+F (m-yq.X$rI?S(˧p yN?D-C@d$h-2H 1OԿӢk_;-󶑐# L;[%DrA##Ӷ4͖M*)o%%3?.9vݴM8X^(V<@Eyr$ED4"P~$,r'f[yfYa#p@5H#}[B4?<?Q֯Fވw`WHɨ.h [ݛgC=0{(9iiyuw ʼnk7B~r mro!em\(߸WS±:qxq2Œd)~=rM\] o,nwM" hl}injwbd7;BʪpH$uqIdOgi:ByH J?njtV\\< nR3dr&zu5;V)ci. p8'<Ȩy^P jNG_ʐnnlO(͊5e j: o%zx)UPḦ́f׌ViS]uC ñ$m>A/RAo?%]HA~dU"{շE@;@<`uo}}2;X q)'R?3ժfo] v7/7HԒvZ1tZPU!w_'{y\ Ӡh%'V(vo8RJ0OKqˡ54O)h7Sx95Vv\4jbܑ{B vNs.|QaṋliCaWm.2FF9;Otjf+5 K [p2RwҪaE6 g$rXDWl {@'Ոi6a"aڷvӷzgU$Ӭ"=3u9bߏZ@ֵ i6 EK>Ԍj+IOK7|0 NsqNEgf@ngfSvWohRIC@^o&\X[̆:UNǽhih&x #@l<}Kgf{JTx',뛥v-:" b}OԒ4 ސo~%qywonٰWi*dr\|5׭o5S kv[R"3ڢzZ߳4#վξO*/)zYߣ4׽,clןIP-D|:X?G,}\'=Ի~_KOƏX?@Bt/X?G,}{\'~Ft~_KOƏX?@O3ѺO?Z/X?G,m]'&eOּ>OƏXOƀ=wt~n?#^E ^gickݴgh뛤OѺOT#?m?ckݳGhIꟑ'y,}{KOƁ ~Oב׽-??cޖz?h'y,}{6Oƀ=p?iKI^E ^ghv\'LiwI^E _?G,}{] '&=R|FEgima?HXZziwI⼭|m߆|_imǏ?hךx xG'Oם}0[ixzƝ,꟭.89OמGoߦ?$7ӿ9?Z]x{iƐCJz.=R:q4WHlO*Y('yx?5B7ZHG?@O?Z7I^L|Fzq0FkOƀ=stIOTy',m',mz?h'b"zZ߳4׽-?zh'y,}{>i~֍s?Z/X?G,}{]'&=S5KOƏX?@O?Z7Iꟑ#zZ߳4׽-?ѺO?Z/X?G,}{]'&=S#zZ߳4׽-?ּ>i~ ^gh7Iꟑ'y,}{>i~?Z]ב׽-??cޖz?h'y,}{>i~֓try,}{>i~֍ב׽-??cޖz?h'y,}{>i~n֍ב׽-??cޖz?h'y,}{>i~Oʊ?Y9CBWkPvjRJ)pj:umeo V85a??ʮۖod\:)pkAc Hs1W#U+94R d}EAӊ(AyVKKWIP xf\Tf+[b3DӇX #Ti?ēj8qad:Y1NåkSfv! ]/OSHh I ]4E-/X1֖T(͇6:Nm} \g67uvCQۤ/գX } ;{Yٶ6vkg8>P? NQ@?)3Ki 34? Oҗ4}?((qE.sLhΈUEV{ 'ֱ~ZAk4b=˓k_KC1(H݅M!QVMk/⸩E>?ƀ3IZA/¤_]@? ťm͞n#9t@4V#??|S84`V#?K|SOtOMVi?]Lm t0`2i+IK*GKOk'3H TTkp~ ꦢ*è"\QJ(%Q@Q@Q@Q@Q@ EPCBWkPvjjW2InC{3[ 7^ n@9@6Co{>=1Iz{P~==烃ހ %/?:C8힞@$jb \JPy1(ӶzRLBiPrs@ (`=='H)QN88ސ?ހiShh=hGP ==aEQ^ק.EB)I@M&={^ 3A8R fF8cހ 1@>}s@ 1aCKzJ(ߧz3=Oz)nq?Z\jr>*$qP)w\R8M!9◷}iy3MOzL?'>:PG&hߧ dt==sHM%.=J9ޓȠgOw@,>4=UOSߧ==]$⫾dį_ǭ> Р s:qVA6+9ތd9LscK3n5Q?F+~>0r}?ΕEumCUWm[9``V̺ zU9tȰZe=TXw)QO1;H cM)(( :3Uj֡!+ZٵpjmV doG(JϑWz@>+8_8q czi ˇ1[o͜J.1A@Kczc5)b2a,"a$21I"`gm޹`sM?Ż@џ6VlW*]r1c# IcJϟF:i} ;{\: v %gd#zc82 $>͜K&tg}:VKzT:P4l$8.1AB&/IBv<֑4h:- (&J͟HF=[nLdeL:Vܮrk*09ϥa>oj笙N1 ԰4duy7n^1ANva.FAgKwhF2aK Gvy! Ka,gi ·$w "PdI+IH޼cF50[d'|n\`W.~n٪\y29ߌ8tm>("L2d#ʝa W?8^yҬy/ *tuuaTt+h^flIH޼cLDZP4e^bn\v5i4·C-%#z0dHt;a23>y)qЀ ib@tKxI37I%#z0dHKxʼFϦ5C@4Kxb̲2;`Kc /Y&/)qЀ h \P32-(Z2L[x?0i ,!*%asAI4hF(beHtkaxʼ%#xϦ<֑Jn8c G8R7Ic/#_?eϦGzBsNgCQ/-%#rʈ4kxʼR7Zf> (Lef9Ēޜch0y畁eϦH > Z6FZJF?$50l3?Y% `Z&gãCVYx~`e /F~yHܿLW4X 4{(atdi夤n^;`Qe /g}0Q`3졉УLI,c?0hG͂ U~N hQBm1㔍l?0hF'iHܿL?0*Z@gFYx~`CYC g-rr0GX h0m1㔍l?0i ѬѣigLC20 VhgAF'8~AC !F͂ _X 0i0b)~`a,ёg̿L?0*УBGCY8~}" Fb6 /V/N 4(axm1~1I7FF!~ʯQd4{("dhvnLF`R[0Dc39Y~~`Uޒ (C/ 4iPѴ35~4elN_ZLr?h,`g-s`04P}&39n1H7FFW ]Vgl9o㙾dcH6F[`U)0i6QD1 l?0hH7G9,?  ^4([6PDc39HܼcG $M1m;8 뚽E+MMXZ9#++>iċ)178rǎn z)x!I&0Yyʯ2ChAhJ?5VjU@Uq tl~O+h}щkcXQ4 ^4iN)4fq!"0J\T{) ׭GvhT{=7AT/a( I( O9dJ޷f*8jU+4 8’>??J(rAgUagFzS>ҡSwVT/(amA}ꘞG(Č}QpD,=xVsXRބWE$7D-$$=qJcbo#ÉQEQEY?%w]V ]gf:ϔ؈>+^[܋ Q 䏻ǿznBd{7 biK BApi !jBhQLsLwi4بf*f )jw"bqaŐV^Cv>ըQ4D@5,/pU (]V O\cgdX’#ӧ~Iϕ Lw>oI&iX1n5=Fn{A [Qް[)6F PK:zZйtJy<=ܦ2ݜI".>Rv8_%n}N?ɨDd-\SıHri .#wXQ҈@RfWg߆T^p\ǽ4&nPs*c{c+GZ$n8cT[d#]-͞y2hÂ֥ F*ұs 4 "c3?3 RM.C/M:d\iP X;l?I}p :MjZܳIXzwh 8Yq cSPG 3ސȻn,HpL0?[X$#I'ҁ6FT\qojs(m43&{$ RƛNy?7ҨMp,OMqIy,8 2rWE䣜n3Z\)r"0qǡO mXïMu-ʯdp#SsO@mYjBmi'Ml(cQ*ȄwqLm9m$]w~TFjQLoQO׏ҫ۸6PT{Hŝ{ `i;>i6 9⤕Y#V8`'X;{UXg) I'֪\0BFO,6Sŕ͒}=~CYUi=뱷"Ht\s֚,fnhX:.h( AAQMfJ %-%0 RR撀r$Ĵ7!k5!I>?2N2(,Z(,UZ3UhDsy2A;Ā) ?k`F8C#"|דx1㽖KKԴ?Ui͔_kh5HL^iw{RnMқ84hu'4iiӉ>|^0>8Q]IW ިW;N3E0)]l0#zցLHqQ[Io(sFrYEcpr*-*- F%Nq:ܼ!‚Gv'qPٴpyL$/MH $t+yR29'_UIl<99MYXaEщH}#$td~ԐM$ UnՕqnk)nn#d')-㎽ ?.pfHP<'~dگ-9C]DR,GZ54RE&Hqz8&*s7i6IrBZKKwgpg<\+pqrUAzǹYcp\,ޤS2[(`% g늁[ zszBK(4 ˹~^s;AvaT/9NA#OX'{<=h(-$LI$`C \4bPsf2iXǻs>ӽt:\nS@ۚh CdõxqsYoRz5#J#|d:{_L"}"8sXxb" ,e2?OHtp<۟~Uy0#a!mjHd;֚XWbpGjfѠrr8DUZ:i3= (&k$.199?^)5i4PӁժ̇$jCyA֪饀n $L5 x=?VĤ\ՇFfW9 +r@rjqrxNen+ ɜWOm;Jpy:B}g׹exIhN)EbGZκ/94y3Kn$qX>wu ;3FiZMz0踉3HM74f~i)ȧ`4f4M14㴅.3 H/]kЁnGw*>1-LQEEPCBWkPvjtq #p רTڽGNmS:XvQ~Ҽ6J#ڐxpZiŢaYBxhtYњa`:}j 3 d;ⰹҖQPZ\ո4.jSdN}qրE)dheT+D ǷO['*ŕJg}F[] c*n;Y\:B"aI=qw1VF돥Vq !a0[i<Nՙ *],DZ&{9E=$`C?}OPd±^瞣ғ`Zi䩐3Sm 9!ǯTےNhݕϭ 'lO.N$rA^MUݞԤӚc'sڬyZDC;:՝֮}5^  SZhHUѶàvl6w`1*ΒH[*0Ǯ^n 3bQʁެ<hY2’6m|@pe-͓RyoƧ?6 FO *+`RIp`׽clANu6RIƩ;=TEV)9ǏTnZQ$`E =K!Ԛ)'ȭy}F[mpO(nrFWg!J$O_3,nӷt *m9Ʀrق I Yw+#H[69Q8RO> ‘;O(<1p@`R+R3;P1jg͏E2"8 ܧ8u7k;}kV#3 IrHϯ4~[׎B@Æj-$9 N*3y +{**#MvҫddD si9R! ѱ#CRNc̊8T -6zfD R$*(ST2kyDl(c ڵ4%H7n*<OX5jFYѷ?4ںGUs1BD[?95T$<^EOw͌g6^h_˥_ŽJ"BL2=", 0QTP\h`gfFuP[$}[Ie4VɹCO_TueFN,m[*YUv p?JWCL 5T,Gvg#WT `90C9RBmOɎ?38rRCfrcpTp?5^=.qXbKJK!# *\ɴ%r!vZC FbYrT3ӾbAof2q:U )u ͐inFy\N(m枺q㚉G*H+ 4슪0>ƐZW3V.-dzCZ8R}3C$~U*˙UZ/,vSM14?5@kv%c ]̨TzW_'qX /=U:R)QPXQEY?%w]V ]gfށ$4a\wkVBhvq׋i-ft1xysҋ>i 2ۂP^\*9;t&mLCjvyDK\Z}>jg0Px\,zW%]^T8 Q2p+D>KpHGOҎaط%u<$NFzsv9-F8Sg!b܀}eR0~BKURTV8YҬؼQlǭ@|1ޚ zc֗99n K%'ts#iPǎ _CIО;=J6BgEK8NIONvb <s.܌t<!p:Ӌ0Z@>L_z*Hѱ1]"J yϥCF:e@DV,UGO\#4`zEc=GJqzTc*Ls٧#B={{Q %CH j}OO8@2y#Ի2sF q?ѭmRzR|F-2C! l}+oKwKm9?bq;|b=*#+(q''ޕ3$捀9DHx'9,qҴ؋2ÂHԆw=Qksj;a$uـÜ[1Xʊ`ך炶3a 2HDQFRe&rq#›g@FKqgGTvwq[6(ݔ.@:rz6D_ίKM<($=(f֊ gT!@-VZ-.cSy9uUz1aJ1knkqdFp KpB }:?nڒ1Vʿd2G".ZGԿmwvcvt {x< N("l08< p3\yf~1? D#Ml~Np1e{iv-IFHSX^S=?¶jZJ(K@ -]6$TRTj:[jb삄`g8YdJS+gs?` G!QkD77PTaӆ"8 &.x}iqp=uc6᭮oB)8oDZRyL[ Rj%ˌ.OJbtPH@JOHn3JҌýCֆd84ى9>™Doڅ4 ㎧֔n1i#9q4 ֞N;Awqݭ}N7[\g,=h@gKkP2&ZJ&L qӞ~#0|`ҨjhWz`sז R2Y=_ζ1WVcדޡ~cHwg4@<@(ߚfLaKsxMd9J}sӞ(4P ".E,sJO'yFO-z ;z MLa9zxR'EpP"cԛ>\\㏚!nQ'$&.i*&҄s}펜?Ʋo 1y(FV,{DFicq6g{E5"ݜJjcVtFP2yH!#kIo-[.BHnȵu5|ЌF`Ӝg=J5->a8˓"DTNԹDz\2),ٶ29o^V^GXrvymʽ맆4[ (8ZuwpT_N)7Z*֦GPXęINa=Zl}9Ԝ'u<~T@CszM$AC݌qhpqLV^`8&fFTS: L V(Z%R|=*"i3@vʟ,71m,dLUHV&g#N Guܑd+ݣr;Rή*=2HI#>pI$"H+ `^3XF@i˕pGݴ;?ҋlj /c9L~UA|p3O5@KfstqO,˴`jnQtOmow|h2s,̓U4voe$<R5ڙi[w_E&"߂hΥr~w?ʴAjwiĒ22չ>Ƴ!L@@A RT_Y^y#eco`ps=+LҲuI,OozǴLlex"@xSyK%&!`x>nF_Q&DAՉ{e/R4*t%ZnT$qzpzTk h ಫ8} iCx%cQB}}"mTJyd?=8Ԓyoth${幦MDih:U+nf!k[l]ņOB:5jj6ZT0vHg;EPEgPvjZ?%w]@ 9hk?UvX"'8-?EٮXB29]*I7'?ZW*ٖ-7W O?@3Pms#7JĒ)CI:qHLf2@? |+ާDG\8|߀$1֕G#{怱R IeFwXowW86\ j $A`SC:^Ӏ 23N[,Tm|XrNIbjd]/Fp#kf;ϵvEdj *hLw*AJ@?66=NG?7Cp3I#>eWrg~*~Qnxw=HČ*L0`Z]y'bj =\+4r)#gEP<*WMϸjD6-IM?0Ād|c%I8ޢ%i4c)㷭D_'#ޗ|œI/J+6ͤ]IIeo^khG Ww@upN"`&0{_#vW2+6oĆ)&$"q .:lw3,"wlrN1j[+$cSVV$[&/` T°@̃uk{@YC VIA8+6QGuQ)Cc oQkwrڭƦ533V#UU ݘzpOOƙJR=8V"~-Hg7?{cVxמg gؖVVP{֬]1T4_P*.᫧Pd^bIPGoU4 ( ! rzrInBlI:o;W]we"dғv lO'R6njiAPgڝ'c5p$hȭP+JKTgǴe=}^o.%kkyh`d$p9"qeM50F*N[֒AZT>T`6[yOݟl=Jnnh\-ʮpZ=PqB8'ZUϥ/tG|_O1DXo1$X$ ݔ6qƺq=V=}FXt"`NW)@ g#6֐ 8ހ9Wi烌 ۉSsR RLUJ׊@Zwa hLG^֑+sSfվp*U=׏bNx*C![4_J|`H>8BoCC2UGL`ݐOtrr0AT9R[@HM(>j~^i:2 ePy}iE('d?7/vB-#& b3{PXYF~׳IJXc=o#o,~b ҜSc0c {jhiqsb`ςNGϥ9->fOl v"@;?jhWl,QVrbǦriCL ;#& nbN2xJq4|lJ0 O=Mde Yڬ.ɒK6 E?2@PNOJ_==:P"\Iz\E тGS8%AX^0U>9@s{~@‰}hn+!@(y5w@4%U$NF1bFU}]9't;W9&*my]'ק6Qvwm5bhB3ۡW5 nX<Ҿ]otY>}o]m%{$^VTzuS:xQwGUf>f.@_@ֱ=<>HʁMYդhͯU^T$EOotU *8A3D0\s{Wg)Zt|ۙ_ 9L[Zn4ojdLt̪r5X2,Uf g ֲM<`x.O\Z & L0!XlFQRqZQ,d`/oe$A4G)^b?3Aipـ+ҀSũj[-$[!:)b0Y 7=$c C0(C( :3Uj֡!+Zv1 TqMڟJKKtnuKÐUZoӿLQg'_wSq )12Nzqҗq$;H2OsME;/5(Pʤi3W>}3qq<)+ePq&^ISOsN~yL c=B0*H"F}H49aVcZh,\5Ih Jn##4ķ'37ws|RC$8`"K•c,N8!-`ST*X/m䪁܎i~`%Xr2saēVqJpG|T4Ӕ 8+.IӲ 9GJE49FjX&/(Rٽ) XS@4s)e*#􁳓T2,''a<0Y~cS-ծ$r}ưgL]ʻ)e99۠y! v6@sR}3SAN c44aO׵=b̞aܹ%$@Snwz(ԛPḨ8!'ؽ] 'ޱb%##;aҢڅ@a8^ *0pO_J|C\=rzԑ`'< ۸('fq?JB%'f=#c5% vGZvrxAt/APQV2N8,A'5/٥<4N6`2u;H9lj*N!CsW4bQ~ʎkVh_KkY+[[m|$u7Fw.c'ѕ>K؄B0V_:'~N:ҰtF숚<灏ƖY4JQS}A#dݜ$QШNϥaAyec&PN=~ z5Uw rxhRMA-"c"n?n%,)MR{Unlm EAԅ#G6,Tǣ|U'5wQeSl?j&zІ0n1iDlF95>mNy6jaہuqIi1(Xzm)+rcjQg!5s"yV&и؀ulS eo$33ȥ}( nqe4y\_fQ.联?mGؠfq I%30B4dQE-Q@u ]gfխCBWWmQ|#pb|ݟNzV^'/ G"5]tɬjn2u[sdqAXVlJ迕1B>̜yN2noZ;*={TYqjFuv 6zT!D97J9+ri]s51* ͎ldȤހ'_8Hm9Q4 wZrdR*;ԫk#2ڕ&0)eFwdhEja#H7`pA⬈)jyQ+CN wqN(L*7v{Sޕq:LlQIR9:SqLqJ;Zsy9ZLbqsO0NoZP P?ȥ_o⤡! sUifbfrJmQr21T9HL71S qQn!gRG(HėFB08{t  #i'9. .z)ɔmᔶz(rwt +HDWe"CCN1uvʒ}OϚe爝5rQ(? ?Rh9˲!y_yQ⯸|1U/0V-K1 wH_*pU8;3; yJ,AU'q$R/?tXB2> KZtkNAH2>ˎRw V&*Huʤ3;z.+ǥ+ޒy}22j E猒3EA~Ue,* 18;i/C֡ȵ! W!j`'/Ҝx;r*[.j>"$S(_'Z8#玴G[M8B@#qSX vTO8Lz"fOr;T<Px8 t3U\#gSڤ} B Us{Z#EBqyxfg8"_UQNCnɝSJmgWZ;I5e˕5R(a)&;F1;xmcn99UpDQTВH1l$IeZ[!q?tc8V!+J݁RoG˸0sڪ!J'8kY(q-*sngRpTG8@q~Z~;k>K<;jInA(0_GފTЌmVDͻ=Wf;YPj踺eȘvPd༎h+ |Huҭ-`sPJgA3!n=s My2A̲{ztF\)G%c"GZ9\tĒI$! ~TMS1VFHƞ8R; irq4ܭQd 5[˼ 7}A [ yo%б.aBִєُEVbEgPvjZ?%w]@lWrCvӦ{mO BpOjnje#pb[+wR2EK%Ǧ1P ZΕ3P@j13:j$ǞNrzԱ!mFF zR⍌.(+dq}joªO[}}H$ TDws֦6 #W*K9T}*q\OQמ7: c֜'R `)~(6y^s>ԛp8f89< Ϊ3}*6m!\0Aǥ3v E;.lgB=Jd@r2慌 9&q?\`uDr0qDIF>7  <12 0u4+pT.FS̪f'HXy>e~1梌{ "7`uژHKtC2߂G $f=$=rxD[85z6Lu48Zuqkg'"ޕ9c+ uV,ʮN>e%V~gnЬbB $HƳ;EH'K//iW7T;dZes rG8F$7ҝ֡ #mMSCɫF6HAd Rx2H!8V6JX.W4ɰ;HϩTE'1vTRwSRy!GzMNIԌ TcjDRqsYW' sMь@:9Vd敃cB4Ղ⑒i84 zn0pKQD񬯴C[ ջ5g;dc9Mt?hDHo}޴Jh W0<͗!T+W*yـe8kYjSbh2X;kSL8c+}5BT*v8'HkSQUI8NT9]9wPҝ)Lt7qs$KEڱ0'B7Aqۋؚ% qZ lƢ*-Q@u ]gfխCBWEw,q8ē 9nYkv;i$t#OO5$۝pF,AUbJmAiqqΑ&KHJ$O,-4P$KTSXIr0*E`sRnlf7x: qOҟ  O -$KΊJ 㟥ac T ޤWHWP ϯ.Qڊ)&7DRʸ#DPIQzSKͨyi5ΪHM%$ΊYWG*0B N18p) wT04O r<)ERTqHhyc\tUǩY >U9~\9qڋhm""*gqN) O$+>V8>2TcX #iib!TSBF#MbPwH:b{O?4oϷy#9q<(%*1E4I4* dU%GsbW >rQ꣔.i3f&xΪJԑ[GZH’ƥp2rzp9V;S(̣pHpw?__sUʄ٭2mZF}UsQ)e 褯qYa$gס$e+ObFZ|W5DR Kko$:!aҝmk$wE$3Dq 2Ig.H\zҳ\+;:!B0H3W(h-.!iaI{n"yb8Ԏh;IPBF?~U نn"ya?襂HIOqH:*>aǧQӷnĒ΍,Wric9$E̫SXHFb>32! ̠pǟcn5M,$qE$/Ԏ."2Ɵy =A=#WAtPFf"/WUʯԊHKXQ>(_ϣ$ 3J9=:H.l #Moȉú ߍGmE$3E,Ee( @?* bfѧh #Xҵy2ʒ\'-EsѰdO}vUM0e}Tpj=>ZʓDt9 =Vd`}H%H$$;ák=F?8(.v6VWд:ȩXH!8${zL#3ں@>ݫ}oQ[bB[u⧑aKkd3D2ztp!e)(wm<ӊ@ h 3Kֶ0̆hE~`g:qVm OKfW_GԎ+;2N?ϧn*2_GЏ5mStM _S=&D̟0^3:q\8Ӂ!p qOU; S4%2(256%I"(\G=k @Bݱ;oꦐdI$t #ڰln{Y R8`?FHs#@泮OTAEShJ?5VjU((<撊K"vcp?QU AR'R*%+f?y73KE+'5 ãΎ@Ytڝir <{~2ʎF;E=iOsTܣlt Q3@Qod.nޔ'ZMD4{W.~QEҍC{i|rW*ysK̈Et}YD/!ѿCQԅ=;'oz@NiZ0wm R5& dN{TK?I Z@z԰8I0zT_Z@Fi3^ I±!}%yx p~r`ɯNcBJOV:O/`<gC^ I䔟t^)EQ:w'L?4^IG+'QʇsL|}Cr{/A(cFO.yFQ/z_+'7ߒ4IB?ZC"u4I°#{'Q 230 396 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((l" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?^#%UU9$rjƩgvR}Sz|Οyc$~U:޽ )6(Nzwq1>-Y`XvH{n# FR5†*u^mU{:yY,WooNnkmv` (L2O=y(V1aQK:є|gk2+ !Urk0]-͸Uls޵$9vP@*ES)?ks#̅ޣT!\ΓTkSlk5Y>q"Xȑpqvy_kZFMR` |ܫ _wj2q!Fykm4m;ɹ^Ch#6\杮Z=[_çRgcoӭ)Wn_W^m:pze6ڼ@L̾ ?-m-A#\qU34_Eoc7(=8 2Migt Yqߟ|%[ilcvn6}qG6/C7Eb/+̼nXu-N`Ts^dbu!G_U3ޏ3ދbu!G_U3ޏ3ދbu!G_U3ޏ3ދbu!G_U3ޏ3ދbu!G_U3ޏ3ދbu!G_U3ޏ3ދbu!G_U3ޏ3ދbu!G_U3ޏ3ދbu!G_U3ޏ3ދ׺+SǎZ{7|\,@ A"=SwwM59d#>"1/QA<"EgRj+M\ڎ"M *Dܗչ3ҷ|?_ȃxE| QVN')jمC#@:RĶcw5A<"ED/Urn`IJ{ާkGrk6Fj.$v]TٟLmw 3;켈?_ȣȃxE|\sc@:qbkC!k3m?: Q E @m~ivIbRNzcx?_ȃxE||sSUHLX%yGTv 9N[aKje,rѨ?J`M`ܐݜ.;^3q`j઎ 9ȷ"ȃxE| ,!yT|_l Vd@?[NRm}G>LWIA<"D/Rc4;)BKg !^O?j_"( \xo"( Pr8zZ4/Qh?_ȣ.c |[D/SZܞ`B}(:qxrFX$av5bB$|zҕ#zޣm}jun&3a p#m]]a rVX0 7M 3GK^o5- #ۯGi`px21օY>*$ EKo!G\m)W;zTrOo[N=-C eJ" YZ;/Z,R|&pA=i!H4^{xTV Y", TS`]1(QuދFy5=Bf5v$Xkwq.&KsӜBZҼAy{F!6hT;(*abza\8 8=L8TrS[xOuo?G$[OW3aXK`XUJUTu')vuU8ybc޸V״OOX{WwI>?unRͭn"]߹6m:ĭKw춐9,ӌL=R5BMlK&Byc Q, gyZ?$jLFmFJ+PeG$zqU wO7mtȨ6lOU.k"〠y_dr]èig~? ƻ~HG&4 9'Qi5xdYa`:\W ?6_*OlUjQgڇQi= \Q9#=_6P?m?綡U1G${s+{j/G ?6_*qF(`e`mCeڇV(}̯OlU' ?6_*F(`aIo&X7W\3F y߽^ɷ\Bߐ3ZZO}[Aw*͠L67繒FPIY1"UwrUL'"">n pp=xoWrYiq }Hb#f\g+Zf=#*ʴg׵iyߥXQ"e8]䜌/eCnYL`xBBeZz\UJF~pu>UOIuss02C1X[>կonV2Hp E=3օ= y-AYf*ISǼ?ji ʢ{}}h#KkT `ip62r Zy2ЇNK"E+bx\Vr23H> qPO+0kDe@z4v0?__&/}ػ٠ XHfnZϾr4nZϾr4f=>9JBʷ:3kj8+޵^|@ۜM;~x?/V5(FĮtNW9/ oӑ'|;&|_Q|9]_zx?/VShw xh_$g@>A2pWFKW*G4<% Vm90ʓ4u WCoPQ*7- ;N;B;+w篴m Y[Tt119` v0C{u0eKnބEu;ѹMWթD}v//3 -CK;1jJ Sl? ]hަI[?t:w—{z7 }l? ?t.4ooSGogN|Q[)wyw [(ӿ-B3Jފ&qIvw ={_qcgN|Q[*ۏ;n?{{Ν?#ogN|Uw >(}ŏ;~GGΝ Aww U:ರ!={_q_YeviBTz?:x2c!$ c$b1,;k,M O@d ~n]krxNmI/?/?y|ׁ:QkޭoHܸ3zI v]NіH0ƹ֣4Sf@Β1~Lmq=;U)#\g[X7c(tbϕzsBp:ȼEJѬz4|~ ePGkQf#_Mm,d<{q[htL#%Y֯4nŭ2c d֤Ӯ%u5Cc 0Ͽ4WsiW0A,FҘB*oheXۢ.EPSrHGMu_B+Od7v " l.3\yx ASX{K[Y,Iƒg#i˞l_?QEXYZ4ϲ}/\%?ڼx HrtX@\$3@ HYXeX`*{hrs[n'%ǭT[=J$[4k}C W;?Z7Ʈ20#EGg2]%y>d*x >?:w.߇]C%I&U^Dx cNxXHHYXGnhp2nt?t^Jr.Qou=>l,HhPo?xjxbKiؖP>f?Wo.d=SJS` UU.vq\""}拈<+ozsl-9ցd?/QE=@t uG>er?(["b2/* #_Gߕ ,'"b2/* #_£tΙg?ԯrߙy} Ԭ95-?+gj3?Z@Q`bvԵ+8y N kD+t[WŗщLHu0 x^EuJ35͜6wo5RDVG ?Ix[ڍwGSXœD\=ۊ?b0kJ-6em7< v__G|tf}O9qһ2dJR5O48QμXZ|poyad46Sw@|^8B<[-a-mDa#;l^ǨC\˪,kWJ6LX.kԳg=oiv|Ua{} B8z][4L|syzׇ#^4^,/R_-C|s9uyB8vĖ`?⢱y(3mno!6rNU(J՝%[[HVBq`3gEy1t.g/ܪI=I!T8o\7XASB!jgSn@]g:EyЄ]qCrob^~qtɯxʍ:.k-qoou-U F}Fxr#z gˋ+^~qth?8v/@?. :1rO>"xOZ{]sV)5C*d?F /L^6LΧ.}€=^~qthOϹ `C?zj t\̪1PTqjsHaKA\^,&|«VvaՁFր:Vx%.[22\]o'մnbGE9Lvu Gi3FIHluVAkOh'qo./ZK{;G|Rnܽg Ah?Ug÷jZ}:%eBTSVvwW1ŬZU6K=ePӠotAh?Ta-*nx-(ZwU%bSfFFoǿa-*O GzďV2j+q}#@_a-*k#n5qIZ[hz1G!mBb; [bV`^W~!R60L CD4W55 k[[!n aU~~4Z'Bz \gvo?Xƀ&Ah?T5R1;?[梶?h+:ARZ5[v6٫]rSѠ8!YVhU[v ]6{2}2]|Y;x+7MwmzAh+ UtT|Cc0CYڞs$א9^'z Ҵh 4 *DۧF@~?:]MA|Rw`"+YUѥ tֱ6m!f!2=ϭdj~H#+,gi$H`NZ ni$e/ݞFOM쮵+Ku{LrWk>MJCq,VVݘe!T63h;vAhjt ːwuC2+خ"h?޹T-8Ov7G4nԿ7G4Zdo34!dcXAan ˄36ܓ$Ϲ5[ڟƏj#fHu>yX"m"pms5kV`װd=T#*{fQu R )!Va(R(8"( O\*CHUROX{6sH8h߿!m.+ y[leፔg@g~(ko5WmY߅ ?t iVz~ uX.vO^*1_  :,m B6OMDNA*BPR U9c׎8RPshz{]-HA1O$bM +bxiyldF=)|R?-T+oK{᥆gX}ԁ@nڊvJsXٕaD /RYfB&6 uX.vd8a>XE?H&յ`DQSJ'F Q(4+[$ԡt.bӈYi}G\uV օv/u Oi` >`w!Hs #qH䙄$AjП]TK)㺌YZ""f#8 \ͷ.~ի=,P@ۡ Ңkd`*߃\D]-/oP_›CFOًͥ[f18Wn.` idAnHϥ*vhhϩi&lI۰Pj4 K{xW̸y['=\j sG5ޜn,S>"YAc 0OJk|,7v&[5$>V҅6N ]&jMm!Yמ#l^Q)ᑉȭXW|mbKCܨ4WO-5+s>p(]:gTG5%AUQ_IB6k1"3.~b:s῏lOXGw{i2Ik#nn?޻41Mcx30xczj2\=aFl7:"_[7cp?X=j?mmTu7p !-,繗>\(dlz;YT(wbA[u=BH1A9icLd\c8; -S{bF!{r$% pyW÷ɤfд lH|qE)y 5%)b}RqZ_ȝlI{"}3wS̖/D2H+W"l- )nHGxrO=gD/ oQB 0R.w`/0챏+˻^G^_GH4)ݧ#6O[Z66{s"^.FLg~zЅ~s;-!y?£5[?i&gz]Vo4 [֬Xk_ZŏUPkXə9- ]-Қ5^ytE$, d}.~dE+h'|=$)j0YĤubм[omj#$̏ηr E)^6?:H-bqPw Sf&X-a(2:r)VGͥ֫b|Fc*Ŕ+ЮPIi:7FFm/¶"u\E'L |+z ݹ[LǀҀ1CEӉhW3U4t1z`"fJ(i(RY 1pPъɏ&? ɏ&? d{vo) \0#N3q~U^#Vx3Ȧw$V~vCrI+FQ/$iu&bcn`o4v:v?/ʡGhx`qn8ǠW!eNԧPċm#=qno_z̰Gwnbܽv>w0sjW=[]KȈH'!H qƣ!wЖ4%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@(_QI,BFYs`gWw-F⠏ִsK׃7J5xD9 ӫ۞sA3^";]<`Vi3@u"xiL2n(q5~q)\oi!ֆis@Vl9?#,kK4f(%0K Khqp:Fh4fotoxx-15.11.1/data/images/retouch-combo.jpg0000644000175000017500000005165012616075370017313 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|tonemap| 4http://ns.adobe.com/xap/1.0/ 478 302 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?F(9$uSj+Yl&d<۝B 4o=WjGfSmz|sG*oB})~ytʅ#?sk[R'kf{:I7)Ϟ*F5i9.,A\Ig>\-&8%@$UI&sl(l9>©ۮO-SI6w6~bYpê]~U\4u#Hp.JZp,nd~T^UMzڞK nefUV9'88t?ĚGk%kgAAg64\,vF?*=GP=oQk[inLv`%c;'uK-Ad2lN6.;~T׸6EwZC X qXM5ū_mEB?1lӽTO{]b\kOy(Xq̰H*#N=^rh:7vW23HI1U_PN[{KN{$=ʜm#EAQzʲ4=NMWBr\DtE~ToEX=F?*ލ\ Ǡ=UѿދcxǠz7p,oxWFz.^D# Sez(XL`"'a lnV 7\+ XiMuM:@.P*-t.9c&"{־o}uQ`ki)2ZЬ@qi-ѣk}2&O1 &61s<nV ,3mcɵ8c[j .%]2]+n}s[{畿??ED6:\}]6IPn9+ 2Cwֵ\+畿‹9tmx`].HlJɐ9n4db5t'ҵ\+畿‹+""* TEUQ/=E]s<nV ,)yQQWw\+畿‹^pyU??Fo}r=EpwuQy[g(\QG=E]s<nV ,)yQQWss<UG^ʍJٛGo'X.${Y Hl%,W'@ hs=p*u6QEQEQEQEQEQEQEQEQEQEQEQNk똭NZI\(OF amj:{}b>} cH<55N\_o 0AœĚy]*wx浳*Boհ*}K="9?禡.իBlȅiU8TIpWy!>=֡!ڿ œ?MƴtizRF'Ԟ}& TQ(m&AӎƊ5_?.C?:{T((((((((((ˋ,dYݰm#m?M:5mÞGoӓL } ByD۶=;'Ҁ1?%6iZjŢȿ.=9`< -|?gP^ іhPҸPx 77w6zdo(YcbYDA?G\o&??V"( ǒ~HHSdhGaKEQEGY%(F{Tl?S((((((((jƮk!nS=rxqs*C c,}kɩk>ig+qp?k1֝o}t+GlVښcD %d ;[xAM/={AiRwgç J/4ILqS:mhj̹?}9?0j: IV~kROmXw;1HىiGj=Ì`uZ9((((((((ǒAk?#E\=p*u6QEQEB,cQ?3˸ZD~]"9z g䟛g TD&Ȑ"V,oc {3xϱ}q@7n; k:;wk19NVoC+wpp;֝Ce1Wq:"=kG4kj0jZ(((((((((( :y/tFK]4P/Saxʝ@vXлEK M5{&笃z e8t>wsqA#.ɞJ/(.+5|I.B:\`mǸf{6k:_[MF+lu>ګSS, _BAJضKkH#8* <=%x퐇S N7HWpF kHƃ 04PEPEPEPEPEPEPEPEPEPEPEPg<hY%(##`UCQl4-'*~Ӡijar;mW'#0lC7a |zOW,gF|d{cVė ֧mBzc'O &'h OH i?€i}&qr599k/;:g ?:gN߰j?MiQY۶?_Z/ӿ/ +?nt h۳;ТOƗf4 hCf4 hص hG^~?'Z~?' U/[XAhON*&筧?M[h'z~7@o޶{MZ}4}A}@hlo}GOPZy/tOM~\*XE|OC5Mq_Q??,# řI=I%ri<8:pASW? "D|=@{WTC}aP%X?E|ʐz-}<%"){iV??Ex $S"Ahcav_ KƉ@? HZ#kia @0:$>^<;υ=qh4H}Dk4.V KϤ@^z =7} x?4hb:ڻ]?}4cϼ_?&8tTWd4/d(MrI=t]7]i~iFmG>}t[_KGO&2Tj=O&+Кwه_&y% 7KE1[ |h ,r1 ) nu2nch'WqVYr$g ?WOJ7G"a,_ZӤx$ian?J ^-Y)K{ZhO2&fSk.}9URҮ-t a{ Kh[F}vRr8 1'$1 m$Mn v 籭`V>ice32đ8/@MZ=Km:GOOtq2\CPTp}3ږYn#h>@avpN= w4 -d܎+/Yu~(?7q]Ofɒ3#ʳmحZ2Vi NjsЬMqή$jÅ AV1_w$Bh8ռHu'􅲚ͣHfUR\;m rr~\Jv:<2YZfVr ix+ձsޜ4=?onN%ӦMMNAViEM|H?#@YVG2hfT'yMNIkm?L佛3g'o*9ai$m@ϷdCEԮfE{y)_8?.+u;T^h,`iP+&<ڨE{+^1 "2ɽI܎ t1h|7$iʫ v=Ym{cL$l.d gu %-LWs^D{wnbK3c5hvqpnBÔg;go,35rJ= EC=i7tf8E4#,O9>ŝC4kd`xet rFs^m.P#8WUVv[Bˉv,qOZ (Š(((hi+u4>6{%+f %>cqJFڣ p1 օ=5çuI,4ityn泞YPcfpP6w˻ HW1>\"`sh}(+c/-dkxhԃCOjVvuik]*;>V#h,c&z zxH P|A8'tF\(͖-ť1~Hz*' XEg)k R:+Ŀ-Q W[/:+Ŀ-Q W[>?飯K~R5Eg}RH)?>٩Ϥ_M!4Vw5/횗EIK}"jլKi0l3MF(za-$Gb/LbH5M* Q:+N?&=Tn>TSVZI,[$'u#h_E"[[yo-bpO\ ) )@=+B Ie?t}^hbKagRK.'#4rVI|ϖ%{C5y=*vi4g#I we=OgbKvЍ2IM1\/9(#% )e ~54 ( D@D#odzW.᫙.u[aPR۞:tޥgգ&{yd 18i gzn͖cLBj8JlVn>ox߉Ə/X^}ގqq;{K4ox߉Ək?>>Gc+T9;mܾTgox߉ƶ<9{ݽܺM*"(*3F&twobҼL\2ھXgrj2܋_m-MېQyaY?Ң|W,U~O**+7Q݇dJ_Un"ncTpA@Sk1;(V @O\Sqzή8Ԣ^tfKEQA拁I[W_uhoO"il4v oۿZ/YӴK;,I,$[9Kڎ+J^y@qX@$2&dAU85s.4{ x8m >St ) )q΂9vށշ1gf|Jii{;lT7 UjiMAlg#ͷﴍO)! 2c$c^=[ٶmcқxo8θok+Csr|y'}X$^JokNY2K`,QYE ?1}xQB"#_?JHL]Q%U{vE#3ϡ G5OT6Y4Rk-$y;f:۹?Vw575tur>;};GgU w_z:h>GshKUmLβ)t\ =+?|w@??vj?==ie3N3K, 'OGsjuͫ ]a)RGl?5Q7a:-+n;v c2̌MMU5gD #3̌91ۗ:E]ED*7Tc]M QE P 󁟥yÑeHZ3^Cy0z&%ݮw{cbyA'gjoqt= s҂B" j P5ԹP קLc?3o4s[ &90;@p)ṷiE+B$7C\.j66WvVٖlYnAۉRx~{a.RyW0ŽO)p@x(1sI\k֑i&uk]2ªҢ0pH&L@i-ck<'N?k<}8SaO^zk<}?_r3܃E*}vqgQxߨ/9clX񝱁H>/9Z}_̺.[i@FPgܚϙ̉WX)"|glr?E%ͺ]E)r2PjnZc;n=[U; )0vF UWQW~J*+7n=On=O *+4.=Oviڊ0: @8 #S_jf?T̰ @`gVktO,D@Y{Tl?B7ZrȊ&qN^VZ6?"( ;gx\w_msn:Ht; Ы+ ^AZ+֠֙s{% cїE]*0:WSCŠ(04Q@Lȏ"9‚-dnٝz}}e[[ƛv,h#cW;iwV 4MVGHyd2xI據k=_[yLcU94ۧq#J*boaڿȆY$SYr;4hɮC \i- "N"JڏNHwhyb$.ؠu#ܚJiƳ^\I ;ܰ,  ` jLU=Qu%]-.^Q\|wA|A_@5>aMeGaT&BG;OܭP9k; k_ë-Sn\)3SU;Y:|ɗH]: 9R8X:mB-h#r6}Gjgٵ??MhbhVwٵ??Mf 4hVwٵ??Mf 4+?ڟ~P5j.".dw`us@"bDSG&:pqVG-'A7/v2>f> !rdR8>t+ 'D6$4J-kkh[ J)xDEN|Y5ﴋK@[KqSjeNzP#й43Q'!@ }Õ>Z HOev{ dp;˴QE!Au}gbm O\ hwҴjg+)hĐȲ#r "W[Zj6:L'PH䍭+Ֆ?{SmwRm F]foztWUҖj1CL gUm ;{n<˲Aͬ~E퍼#аh+l^-J6ݜ2D˿ :+{>0<}@ sTHeM`8# c5dxdun54s\G:?tdxO7Sdur6o?+b5V%q4cz8Bk#ƿ:kPd}8QПNQn"tU4,[g1NH7gǿX-ƣo77g*F@8P})>ŨB_?j?O"j?O" QA (A (CVصK}V8n&[vAぁ{+hZiHbNY`>-oo7*L25Kyo K$8HB#c!f54[keekiKU@'(@zC_[yOc@pNzuO_*WsW#YvWåO̿y$wyu#gv ٛ$춼.yNN=(SΑ?@==)შe` EyɧbjA& K͏5qQt+p|/+}ǥ1_cN(0.D  Oŏ羀-:ʳ"CNj bLٙwv1ƀ6Fjoei4~au/ q6'6*@uiy{ho4ZeqCw9 sn;v^_&i<+O\_JO[y>wa0mր$5] FkLzm_ZkViigPOj3Y1O7VB[(GnW#zQy& tgu 5RV-!/*e܅)#jlj-T[5|RKw@z+Pr*p}A}܄""$mwZwi/le1FA,:GOƁmF9fccMKUab{eYB>FALVʪsWdluI3H teenC)4fE(.i(w<3 xR5WN$H48o+:dE.i3EQE^6F ,w=Q~V\U|0@ s;|KKL}E dz%vqU[ZTdF}(uXnq.$[ BI Ɍep:V/l5ۛQΩo$nftQgg9z&v>慣Cvu`!Psۡ—/Zg(a!OZi(\:ŭ[X 7VDצj}gI47sª "َs]h8 rX:ڛ E߻2;+Zv}mSgv4Z5E0]xgZx-㺾-g6˕"#?1$BR}D%;+3>v!( 5Hgi;EqiI~rqǵ2šцXZKą WӞLV8<1Zo٬He۰6?𖨺E\\]=J6o7?*h 8 jW:inK% VCS-<vZk^ڛXg8d]EfG4'Ö7,Em Ď~z(0)QEQEQEQEQE^YQa#$}fOji+[KD3`N6?}31Vǂ_/~1m ?*ʋijjSi-彬D+*"qrqկj?ys0,o\Ukú\ͥ+ogwqwn= .GgyoV Vڜ@6α9 Rj^4[cAGCHHlz4[Ȧ}AYcԮ/J*9ힿD||1e,!n;P#m7FTmP0߆kh<*pU܁c^:^cxos<Ǖ$s򁎜կx4,J[ٻ*T/QP2 ][>hdHko @㎧T>*}.K:CpHfY1 s* l[ho'\!sq r)!o594{2'd1zqֲ?M}$:R1L鵳~].gFn@zӭ l0^tFhhⅰľ,q8∖V|l.67%7tPOnWQuVH59y>[quIqKXks{|D$l>U] ("*Т01X1F7qߎ*^,ˤpB ;^ q@֥F`2~ P9rsu1ju'IJy4QEާsZBKac, q@,dHio!}-7 pI=ڀ4Ea]xFӡlwM*x^*'|ֺڔAEp4YrxEY\jPGu$|[hvWgq0VH'#N&tԽՊ9 c8kI ne{?rjHzP3zu/Ia$궐b(&;#=j&˩6;Z1SĀx=-#Ùtm6;s#~tdz΀9kݦ7JDAA붲5*Mf;c|0Tی7;qӭzGw{ΟQ[C×xq^ml4ybžkحn'?͸޷2=GFG /%qw{ gxHc6Y-´YN#ֵ+#U5W*ǩ0QOO@˦M@#ܣxVt e; @m3ƩǼ*i uzS\al`5FXl5q.~Blo^L߅׿hi L` i .I5ӯ:?W~ySZ<,FO h#~tq?:QǨ~tQGQEzΎ=G@q?:8Q(؟_ʍh؟_ʍh؟_ʍh؟_ʍh?z*/HHI터c+6ƺ=-+"#v1611c=#}zG|ټD&=#7~]iI8wWU]SS6>Tz6^yʱn|8.k8"t|:F pG4!H$: Y),)o@8R2XЏP\_V,ZBv{'ZH^ c5]|~S+WOvU=O& #ƿP*?HKg](LSMP~-UFr70-}zG|]UԬGxmcr =OqRv#:ͽʰ'4aKDմD)t2y9;ϲ~m_#~+2Ȋ[HٸqخnN_uOr ^:ij^ 8+e6r>]$\]A]S<1j{x^%s$d6l`}նy0i\M<@|:=6pYou7 =:e,7fH7H$wL۪ӗuZzsa#7}"6H{)/o4㣰 Xn,<}J6ѷT/~Gº=-$r%UP$]o7eiQɳ9hQ;vlc'S{9sc{ ,;9+\kzHnVgHE$d g#L۪ӗq0xC qƕ5LA^;շe]JX#bYH?nN_9[ Wmæigqt+0ٷq܅sSVt_ _iڮpȫ r)9R 88nN_uN~i $d6XC?fotoxx-15.11.1/data/images/trim-rotate.jpg0000664000175000017500000011234012616075370017010 0ustar micomicoJFIF@ExifMM*V^(if%0230FȠ01002015:05:27 21:19:21Fotoxx:trim_rotate|tonemap|resize| Fotoxx:trim_rotate|tonemap|resize|NE http://ns.adobe.com/xap/1.0/ 8 400 1041 2 2 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((," }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?^).q:UO֬yU9$ܣA^cF٣}3v$}p8aZ:pYbQzט?*C3?*"Et.Vq]|vPij©!p0{hYUWB|5هGcwg4}>h4`}G"1Eyg#G؀䌞Oj67<ف(~UmteIk ֨b 7uyOjlE9GGޣ8cOZA`XpU hx[g.g8+Bpy9\,v>szʏ9?*y\,_oʨyy\,_oʨyy\,_oʨyy\,_oʨyy\,_oʨyy\,_oʨyy\,_oʨyy\,_oʨyy\,_oʨyy\,_oʨyy\,_oʨyy\,_oʨyy\,_oʨyy\,_~Tsx~UGEƅQW;ҫX5[Nmѫ&((((((((((((((((((((((((((n?+氷;{v{ID"Fapswgʻ礿 ?!c( yk6/?礿 ,{{կ礿 ?ac( GV!c)?ac( GVac(͇zK.wwZ͇zK͇zK.wwZ͇zK͇zK.wwŻzK2/?‹0*yk2/?礿 ,{{կ礿 ?!1YW_ٰI1f=%QfE_;ޏ;ޭfC=%QE]|z;S3%ͭA#臈c ,|!? u<{0\3m s4ki<,A_>Ҟ'CϞRiʹ9QW1kx;a6a#縣Muumk5 |Elά446-7M4 /lc}sڠ?tOeqtG)/;k'Eu}Y5zy2 K?-5 >[='ӣDp}}hoдbN0N3ts/xSHQ$SJ wwwm` -h䰸G4pFxžY]k;"ҭ`k2)hِ`;W*>jj)u{bf"G %1BqZ[V 82Cq=i.5kҲFX8Qk_ޛ}aaӦ5ٞEX[$S+foZds\y^9k' |=Z,4K?jqZ CI=Mf]xÖ[MsZʗ?28j?xvirX\Cvqy^7 qZwozZ]O UHh?Zgk9keUc'i]:c]'Z$jqyjмTPʷB̧rl'׊kDM>9aA)H,s{Pc. U 3N=2_j5nVL+z\D oC3MdqOg;UbWA_^YIOgc<VZX发8‡xN𝕕Σ`A' ){Xu Og[>m-n[[nRYԕfy{>uKQ,"W <Z֙[>y 3873HM {c[ Z5$>_>mf?ѬzeW%Rw)\}ys?/QjP5$M$ɱ0 8= ^_[[}j3DXH- ~5VhiR9jYԗpk MOgPKv" 1PFrJl^ נ7U͖k1A:>5@^:LGa4YuC[ɨ+0pHb1yO; QG4,TdzcLK,)gm> iΡIrszΘ'd0ǵ[ !H~Ajڙ FcggK"9u[V@AjA/=ͼI|[Oش7V2\)H’ rsИz^im_15p>T2Rx]TZd>1Ʒp1ꗺɯj/Zh!t v9Eǻ'R4뫏"PnZLߐ5|+5 R9fmȸ[Z=2=sEc}}ꏛGE7ޏ7ިy\,^}}ꏛGE7ޏ7ިy\,^}}ꏛGE7ޏ7ިy\,^}}ꏛGE7ޏ7ިy\,^}}ꏛGE7ޏ7ިy\,^}}ꏛGE7ޏ7ިy\,^}}ꏛGE7ޏ7ިy\,^}}ꏛGE7ޏ7ިy\,]irhǀϴWA1'SBe)?L?z_nQ|Vla&)^WLxF I$kuǔ pOn85Nů-LZy)ƥ&ί<%|^ _Yi7g:~2G/?XD_?OY zl5tE? +~RЕ0WecCi~Ӭ"/ Mk?k+ ;RTES28#v5ѬgAXi֮Y"<_ֆk:d)pLnQ :0rQV#_7*9|Ýv2>Ѭ"/ Miǵ'Akw+ iix[GG]۷޺xK%Z-\A&f`a=/s.X^24RA<,HFFA?j1MHv6̣ ޔBVO@xwƗ7iVܟd%.<(VDn~8q,C G d`|qeau}[Kحmݝ7_P>"+V,h)E=ł$LD\LM)"8Ա< ωpťF}r.ua42ŶHYz:]I#C,&Uu. {ʁcr vkW558t,V\.)^3^s/ĝY|3mLu l e $cGucXhF@,5Nњ .$_9T*' cx[4ZHn<]Ùfg 8y ?5Gcήΐ?5 (}d}X<?>Ѭ? M[׵ N{Zv+8'D𶯨ajRKu;m 铌dthZ#3i ӼcXk^?eTl,/.ܥm4Qp2O9e}XD?dڍm-$p/,Jmqqwŝo6j_)FSniwgwSPeڑaJ00eNTIPև;++`p' hlwiyGֲc".A9`01L{H}g@Jүolu8^BP A}Eq;[mj3[0}@B$gҺ_VYpVU(AV"4F+׉5MV6*}c+8#pNj?xIg k:\HTcj}vkƇ-%Cj\\EmhANGj?ls]i򋘅H~z)?ƏHt 9B}F猿4CVmK/jZޗ3Rw=fROWx9=K/jܚTV3GLG_9g2V!+chG_9g2l_՟Ht Q VmI/kf1$2$z26A.;6y{mzI(Sv猿l_ՏHt ?!+chb猿?5c?ƏHt 9C/ive upgȏ=vYDFCkM>SJĶQ{k?l|E7rX$dbQv{d]$L߇]gaۭ5!#,O0q;O}gciQjwu%ս؅?cזڗbo8}ipOnh:WM,-Ī0P.;`wz(taUuqC^sxty-HjU/ r?*/t־ln؀k2v4:}uHD-bRlcJfPoS dj~o!ƘaEX__Yk2I>l96p 6ZԬn-MՆ?1hr2&@j,hD!Y,zz~T}ae=5HBeut{OlR;&I5XuiUH]^ƀ5.}oT}+ _[=_j:M#O{ PgTGwW2M,q"c?iFVa.d=V$B/l.m$fT&8q4 K{Mo0c2T:û=:luKeҚCag6YXcڊe}cϵoVk#+_ 6WKo-w5.%MkQ@W(X'Cf[k0+m99r}+TW Yeƥ[LD<]VOl:]V.ڌ*D~UYֵh*M} 0>x_I tJ/M@f6̜' }צеMwMº|66ǜH|AeiPMqx^ NX[ y.b"&T=}k֟PQX,7jqA`.\}⥀pmެIjdErIq?j÷j~.׺N U;dXLuYG]9]ܖKlւMK7v,\4x{zfw,wq*YQ'UWmO$hpzha_/OFj51sA oa>T9?g|WZ"G`M]ONSdyB\UTOI Jh$vw)FܽWkr=1X_tC_td[{n-L#4j*~ Mk'\.ͧz3no%B;۽Hj>-#]~=ԋJ ץ0;,/ h@m-h'F!6<'iCgBӊ@kO#_>(} (}i|)Nffk _Mgi,\vq# p>ƣxM zƟdw0a鑃Hhk-/Gh}F)&XLAS{%dZh#ItH [C`9ϵyn[iV+VX5% ~~<@3Z |h0  2IS4wK)`F_Z!!Dn h5!^hڀ0m׵E7)KqЧ_b?⢻=ǥ 3ڹ[ƚF=GKLe{oS^׌}{MMOo,w:1Gq3KKon+!`ƭ/ v͔71GX k7 OG[U i_4>z TWf&]j=O% "5\T`]W6Hr2: *{D.cyLi9|3 Mo/Rhf77&p!R=_죿hcx 'o}hnƼ~;Nck"F\Ő|sϭzE*`Y 9X:n{Uޠe[98@Tm8gE Ts~a^iͦj #[Op[#޶׆=jͭ*=ڹ|<|="ok."/ 2NG%z$G5BHQܘ|Nдy0wP|?="a ּQoy?,/5(n&w6(Ĉ nF?嶎ð(XXbG`Pj.@I9*SJȟ+Qc-;m'OtkmBtWtᶫm'>%H?mo'z{:)liVYܴX7#FFPOek bTzST#6궩5«Č,lP!wF[縲%7/Uǯ"O|:Wvw6ax!-d nZP%'5)b s?Z]/xJ}CŃ݆G`( l|ZZ6e`OGLSy[ٜv9[mQיMd]K8S|7~6|Kt=)|U+ۑq;ł~y;{mY[k/ӽsV^ 4[Pm7O&Qm7*ON2h?u=3"/-b W@kZnhiwp@Ў[HmM%U]Cou+yo5;EBFvZ0ۣ_M-=fi`dxlč.7g[S*H :W;Hc&_`S,#R{n#8卥 Xs{[Oս П;STuG8zq nm}S@Hf6<l`9%uKQnowڰ%m["/1xCRmʎy.ezw==+ks.4dn%Bx13t3m#YI.^M~tvB6qV% MJWy" %v6wͥݲj뫵NʘXѲ Z]k1]Emqq&,cJ ~Uy掃{o-7Wt,F~xGs/݂0c?Zܶ.wk| J$z jxHҮ/, Zng7cI7oiWS]AD3ÞM 觊m8rQzxի/G@2i1F('&^V8mX-&z~hVtڼ I;?Gʸv}7Pk1f~?k?5??Ɨ";. TG RT ?M!\Sx*9mE6E,G#$<:隟ՠ8dYY*8cEř#]%ͳV@JzzץשϽ?T?[z@՛z@Qa.,g f1# bL v>5\1&k-B]8jY*gghE΢<)?3m8V>_EP0 JE=m?U߁4ZYC}Rk/"j??ƏV^~_eRGV/Sifp"\qpH0B?SM[5h~,%*Y X_A|f Y~yxy">@~թϵ?Gڵ/*ڭ@O jԿZYCCU߁4j??ƀ/R{/"n#)"%0r *jIo[pIżn"+$RZb@6Y*˜G >M_EPoڭ@O jԿZnEPonנ p k(!P:OS[ GұlG03ntu*qЃ[I@f^ZGHм1$UH֏PLX"sΣgi?m)vԖAm 1 :ΣZi|  ZI @P+{KmuvEoV*jzw|.ږ*ټM,2+6sPK4pyH~&[L_95{u FIw9?!LD^$A{֕ n*I$ GFek}D!Oܯ #9i>}V D䁓֣ռ85; c` ۡ5<Tɣ7j/h^ZLXP#aV=Ff?uAjqtK_-_ol{S/MZk%H$OȦ6V96L_-*=崼0>4aԟ\pAjMBk{6P $-Ju#!:^[(kf!%QťߵcJ򀼹p7]Yj]6$i.'v Zg_.gD7G$jHI?ih>)51Ү 3DnFEC{-YʺKʅ6 ʫ0TFsLW.,S&dI0#Lgym/ t>zyUՈ FjF8V>C2gkvιa zISC]hGh~Qqoo夒ɰ%I%M {";c) 2VP2~՞?"un;{P8V\Tm>aP"=R(D{O}g&ʠ:]BCu*2D>i6vjƪ=hwwc${]j\mo#+q*?u [zmNzV^-?V4鄏>0 9㏭1B][[mErj̧\bVPxCo1"h@(>'=yjqDURMĸ'VkmLa$7}ym#ʠ.trH܀KkOt>uXu>I+ki4ki5gAzZUО^0@o,o,myD돼*:rjWm_fLҠX36yz?m76Ʀ\C0| ʴbmP3iG,{\F\)M(=if\QE\Tު5jQzx!ek#]b-p2߷V-k@mKՖt422oP1GQ2 b5,H :8+qĝ;U&E4WKn݉v0^Q^U4Zμ/cMb, G ut[)5N/%:@-7^Pcws[`y=MMV3uQ0~qW7'>UU<>|IH'98v EAEhЀPHvhx;U㼸YXZBI9V@5%v~3xsE!mIo4YsF VVS \wa%S,A":Rp.]ZXkX)lIih`@5_Jkh%o"%̕3`c$5omdg5{lÀֶ Mݷ2  v?ܳ˜!X7ݯ:ɭ./'# uvڬp}+vC)_ Ƹ-%ZꚅP0-}QK=VRFKuKdqBy ӞY۫kke;]xaQQ]h:ٵ!N.O͟eZᯃA5S5] |8S@γ&~6FH P")]_ʺ1\<={^Fڔ*)4WZKrNa"lᙏ u>xW^ilegF5Q=U>:rZ]Goqcx9PF^?[EZ:wj-ptX4.7u$/*<&q;$uwDןiO54%ĖVBl2:ɥS1B7FOswM2fg:Ұv->4TT 8i$wi4ڦs` #,1:w.a"MO #$tEífYTƭc5gX-. #ᶧh[ztzcϭ4kⅆ]صĖsmsxBTm<989y׌~+˶g1 0 `3&1binmEG/[ @o^jhI]l#e:sSx/ kP@ݡq%y#UI}yl>M!\0|Gz|1^^Gl6c䓻$&7+rr聼1eQ d/Y&h7ZJN[I8#'iK@:|%(!$28;$ ⟧|Eڥ]>n3ʢ"q{=M΍siZ$V-s%|ǘIW<56:ϭiZٳHdZFhٷ20ެ3 9x o|==4RDU<0Ow(P`H/=,~ҷS/ұPݷo¿ l_Q{VZO To!{c$ 99c֐X'۝1o`*ϒ=V6X쭢~f*kH U7q%b&٪_f4~%mO5BO˅ fL YBs 3GXas]n?#ӵ8 eᛛvIY#$qy3u5o-:AQ2:߬φƩ-N|ˑfU@hBfU+.s֨ WRKb-Ip^9 ӵ x3]-gҐy+y;!%V4)p+O^ѡ7֏Ki$&7gyX ?ѿX ?QnWe2ϨmVyN{kk7n!v D!b9jl!Bך } z!L!4d".u%t~y_EҤ/L]ͭܛ1ϗ cӽz'φi s/m W:a'3MS{lR$Kgw#ցZBKImB*^IbѴOW>6O}Q5GmnbN+fX 3B79MjzO^,moB7oe;–:c5թGUw45RCtnMBgÒg''åX/>/ZI{= !'unc}v 'S55%٧/ #&t,g39>J[Er6\@f%Ӯ+[G_ egon.QŔrCssSEEDeajy^/ފCyr.O>VA?zwkw$t4#o?Oҩ젝$kh!QG@3yp>OB|HѵJBth"JAA`uPXxƩnm>|q/;9m~j>?XtvLHx#Z mni'm% .r0W߬w99yFPj3e#`U#~ {5kZ\H/Qfx\+׷ah߬υ?V<ᖤa!kL]/JGܿ+oľ%MžuMHa'3^X 3Kk,?9y6ML*ZE4SX'>w-Mŏ++]BFl"XpH_|43GܴkveF9%W㱧piIC\QJ)qHhUV_RV Xn4-GGG$Ώ$Ώ$Ug62@2dRy|QG@ yA?/?#'O|QG@ yA?/?#'O|QG@ yA?#[F7OR}p*O:?<([&I)WC  6^FONΏ2ߔ 6^FOOΏ2ߔ 6^FOOΏ]:{JQT`Pma6_dۈv@(jE/?#'O|QG@ yA?/?#'O|QG@ yA?([?GG6BHdy%|nw9')l3[ Wb@ 2z#Ώ:?f~S(yA??Ώ:?f~S(yA??Ώ:?n~S)lII9( ( 3<(d$xPT:t=(yA?;Ώ:?f~S(yA??Ώ:?f~S(yA??Ώ:?⹑J+!ESJJ@:~4:?<(;0{Ƭ+,H D _hTYg*i׽aj)Ҵi5a*Nq@g(̿Os &jyR钤3ͷb?t ipoAm9w3Pۦ 3/#Tm}f/~o-3ys-/J*8ր$̿܏?K_ MKOydo ޢe%vCl mif>ǧ@9}̿܏?UD5;$L= *}S5 >#wb%F`9N=@{Pe 3/#Tt\GYmYԪBsVgh d@tO}MZvuD$H#nV٧EYC=9iACXoZ2y(f 3/#T7:Hf%$UzpNym^j\('g.鿹ŒVmi=!Фόr1xMҴy5;(mV*]玹 Q ּSG&Jzϥjum=q=sҀ f_G}m/eJ/ճYz'tPjmӮ~"f 7KTR6\CoĒ˵9SEqo-ڢ[W1랔g)3/#T'TӔ:UA#=pBxaxI"چL# Wtyg(܏?X>,.GKR'0,Yvs-cu<0!`pd/S?F鿺 KO(࿳Ʊ̬_pɮomMZ!*zr(]v?&e dwR4K*# *:=j(5]6x BTfVIng)-.`Mi<3xD"&_u?њn 7KSFhG(/S?N}/#SFhg(̿܏?Of_G}2r??4fŒShݟU5j#4@isMϽ@Ͻ@֎}iPZm}h֛E;Z9@֎}iPZm}h֛E;Z9@֎}iPZm}h֛E;Z9@֎}iPZm}h֛E;Z9Ӈ^h ombJ?*NzQ VDoDgry9'xG\t :ysP.iS~(ӴUִdžySQhW Ied 9 ϱ S( <[xi >p@\:V d޴/ұ,paRwP=km>P=p^5]ljSmrdXlHؒ)GQTwoHg\7ԭ|Ag``xŐOFoBy55F[봂]ɮC}ۆgTac%|@!'Ws~?s]W? 5-mSRyyf̭!* c'q]i7<_W7l&;(=KQ $\0FldEPy^j7)i6wk<:|L<"?>NÎ(5ou-mnbȎ!XnڷOhT ?VWU4 }Og-m-l# rE8?u%o5iEoKbRvo?V? ??c1xT~'N[58qJNE֦U-&'<<؁϶+b՞$hFz*yb0uhT "Tv/齊a1. xʐqҽ'^I杨F2H3"ÜmUng8QSc՛wMb;+?> n[]bS͒4m{*ѭWZx.%`Wx Tqӑ+ѿa1xT`ָ٨Zx桪\+->;Ʋk#f/-T8/L WY#,,<&%l)NX{[]0̷Hq 3VK~g|8m/-l-:^ajAw2ޱe-=!'mE\W c8ֽiI$z){vW2[ϬFdaRJGS):TizE\7~UNodl6l;2t=8J? m SӐZZ@~гFVPeq0?ν/|Ap>EY FH} 5%%ufCᯇ =L_D!L#Y|]u/$уNhbPhmK %Q@Q@Q@Q@,ZTު4mQ@Š(((((((((((((((((Phq7v}MhϹ^?xY]acIan-23]p08]ض)i5CUfSARt~<_ƕ{ iv2.==R 8uu&G8<ֶhUo}PBxX0n&_N踌:,]6đr3EQWM,#'ЍAVn[(܊ďM\[G`OSE+zޑ .4/0W(+~+kM6SӦh#Gbаoզ^QLRP9/"I[}4{ǧiZǧ^&uq<ES~xkź Rf !t/ a#)9toxn{k{ytkG JTnf_h%e\^Ja14MC.{kpY%NuݺLv =wi:~m-֥yh&DY 00N{ <#C'd!696_xz[ {'ҭ͵cs9c<;ΞY[Z\؋IqpvvOzv(t;k<bE,ṵ9[F9X_YƇ4mu3']גևt;I$$giU `d4O6 ZB-J-7J}ς[oLb[ z3^iIш =־ǫx5,[ EhfLg- ߎks⛦ӬMh͐x$fuEJX%HH_82}jUtPD%.X}x--aҭT Ҁ9?Eh0Nb$ I8EuoM,H.t$턔=7qoyt r2xg9N+J]>?6eEx"(Zucl_.50R߆k+z^"j]ϵK;20 NմX&lˌAm5k{-e_zq]V!ԝ\L]7bx 99iaaOxn}e5i,QRNHݑ[?jMm`;S>k?b[?ؠڂmap*?&?kЏC/Pv,qY,b9\$|Ӿm?b ?!yox]С[ D|G9Pzq^O鯦]m2S/#Z/{m3Z'@ 9:o-Tn!2l8G>\qY$'NR{}@DLhj!~_𧁗Z宣sz|w{׽`0 隵ͭƣg֭l^gk^/o%">ȍ ĉ3ΊGAsM|C5 >kH5(;LxS]hC6 a Ϛ,&ۏ=r3M31{bAUW@<i7r[ݥܶ,Y !y5~eam <欚-((((ݟ8I$_9"Gh:4f!4~fFhGh:4f!4~fFhGh:4f!4~fFhGh:4f!4~fFhGh:4f!4~fFhGh:4f!4~fFhGh:4f!4~fFhGh:4f!4~fFhGh:4f!4~fFhGh:4f"4~fsFh/Gh:4f٣4~K3@}?ΏGԹ4٣4~K3Et}?Υ~Sf?\Zf80QK1h;K1@ )qECq7*4UzAMOmlrtc+跩ufEEDU]zhM!5Otl?G*H{PHۜ2jM*-EuwI]<$ +\yssگٻ]ؑdla9L:ldV9#m55+*? </A)-A~*p YJ9EUQrpz;QX^Kx?)h ^4msjZΡtHk;C23AX/8ߵ J9z[mJ-mF?¥1@ek "IC1wګ8'5Ot0P^?#U|Q+Oio{xĜ5ނ1rvDNj ODX٪ϭ?f>?ns:MVܫW*K_x@v [bM7XnK;Ў6ym *$E9fP?Z)XDX*sK "V5DW3;SFh4 Z)(4PEPEPEPEP/G6_1 ((((((((((((((((((((((((((,]cG-[5˭w ΧC@#z޳4}:VRY/aI 7xc89Tb}QXjˤj^KyI889?7^1oRtT#@u'vB䓌Nt[ˤD[1 NJvJǵ[zzl_hX> *xҠ☏?~%jw:FWmFxO΃$ukFڮauq^ZFPw6zc?n4Ėу#,g,xKckfw-K1t9 k]i-s2Nu;[8n#IbIG%nGltƎmb!vQd9Ghikl1Ջ33bϾsGPrZb%5 m$C0m, B'9FY M&-(9cԒy'"路5'=hLl00{m=sڽ&P}:$HUBIfz.i+#Gw[u°>F/5صkk!R"[^G8aȫ9sN Rpsdy\T9)tJދ1;k.#Y'~[] {/ʩ|>k)1en߀* {/ʕvQV2ob٤4QEQEQEQEQEQE\TީCeSb ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ((|L!b F(Q3E :+9N=*˓06Y#`y09 XPji:c[N,{(]<Ke_KeO KKI,*-6X./g̪NAN]'LR `@V (G@-gf?4kZd[YqӆfpP8EE DxUO6ͨvw1߼{etŴG\`~7Y 4'=;5tDc8E2Pk[8n]S n zQqX Pi?\ƽfM?ciVea9r;AH/X8"ddT02_;PAzG °:|zv[`қYZ]u6ԾƇ϶Nq]޺4k?lp6wIwQP@r9E:x'?Pi?4tQ@٤gEfΏIE}O:>'?Pi?4tQ@٤gEfΗE//eRe_΃袀҈e}h/ty2E<}GG/袀gΏEe_Ώ&_QELɗ~tQ@ y?J:E1_&_UeRy2Ώ&_QELޣ<_Ί({t1džsE0 ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?fotoxx-15.11.1/data/images/sphere.jpg0000664000175000017500000002075612616075370016040 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|tonemap| 5http://ns.adobe.com/xap/1.0/ 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-15.11.1/data/images/navi-buttons.jpg0000664000175000017500000006334112616075370017200 0ustar micomicoJFIFPExifMM*V^(if%0230UȠ01002015:06:11 11:36:35Fotoxx:trim_rotate| Fotoxx:resize| Fotoxx:resize| Fotoxx:resize| Fotoxx:paint_clone|NE http://ns.adobe.com/xap/1.0/ 8 500 1000 2 2 0 C     C   lx" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?-_º희6[64׎ߋ&Qmrl*0[8ŒfRf>J!`x&4{Ο}1qkm^2 V@ܜ⼌F W A֔z#ngYYmw?dYIv|?ݹ ㏇=kC_x]+k$u98K^-Z٭ƛs3G18'r;~3׆/?u\,Fqڷ8 XcF8]^3񽅜7%V$eG8WPnTG-HZ\mR.mnCxt1n/M/\g'j_.Y#W} ,,'][LggY6H "19On<UoO|YD-s 98?mXGWGԭ5m)a{8[U3Dd^+Ok^sgMs&_xLh[_*eV 0=CzR蟴W:ڎlм#x762~PI&=oOxulơqϊ؜ 3wJ_~qj^ŵ/+w"{uBԏ08tWx7ㆻxhZė9vgx"bN8QyN08WWxC71[=R8 v98nGjoIzKu:}V\[*ʥ:dz+ŚO5}BR[ m. sb:2dv +??h |3m?Kme붩yơF=q^|6:{?MS]l[nc´R&sуTW6[fnC.}VP#@,d]x7Ok>"t.aXWܽij@X Aހ= |QZjE]Q$RPX|k㉼g phFJTer$ح~%xZZ&ΖwpYj1pTߊh;|;{w'9/Ǻ0v38PjMxP߇}(F^ռCxAt jP_ZHzտc7\׋t}sGگoc02w8^K;Bŷ[ZEFJdmb'Y45 #7EIևI{vm0=Ny(VA񵿃/v**hƗ.Xe@Bs wTß|(,,ݵq#,˸ ]ƀ=koo/(';imdډنgz7>5xᥞwhyQ%(Ize]ZNH$FC)Gz<7B6q~U'kj|A xCUхցK}a"$##(I0~1@Eq?|jw:u4{hg/c/pcg'?|V[h'˼IsIphO_t! :v55¯"K#͓+* o7}~W85=0kUŰxզ,UeJ_SU]/Yցj&嬾wGqʓF]Ɵ>4xEе]@j7,Ub wUN|@i%…e=,G>?u9ƭ2 %,qpyW򟛧Q\Η?z^OyVGZ a*!8?8T:]@9OE>e(SQOE>e(SQOE>eFE;{K_*\R&\F5|/<u,CL69PP}"h-'lH̢܀v'־/Q]7l3qǽ|mZv`iTvZKGz)bxۘ6 Zs^<]gOEl[H|,gN+JOO׫GQH0BB:5'S|ݸiwn( #=sxWge[6n}CKA gŵv'۰տdϊVvV^^MΑAoyTrO@Oῆᕦ8Gu8dы8H$ԛh=q4"KGL}JR^;b35_ڭjql$0/%K H~N V M/ߴto5_aVK$f;yYQITYS`ݜgޙ|B| ]]_@ѥM䶑 "Ԙ豬Yc}u)P S>>zKx^ үΓx[j#U#l#GZ~}~5׭goZxn /O\`FJ;l&E28V BPNpcyM>-=Ų>`}>D vɓJҾ4_wZc_E?{چ;$TY!J0ʰ#b>4kXW_tJv?hෙNBQ(#5xƷ>>oo~k{s {Epͫ#tE :>u dNӣ-&H%vBg('$JYCB2(Po h/mB-V; 54xy_[qs3jڽΧ?S5\9=,cwH}/u3hbGg}*=f_ u?K,E ̶xS Ni爂>M}k6} A+StdYDarkwFiԯo.%CKs%LJ. qLg8/j cŶ1jT'&&WT,1r:}4P8H5 8P柁4WHxa0ar;P|I >>)h;s+HӐ~gݸ3r߳¯Y3F~j$nB6~m7a}9慅قԁɠu7oWògZ)j/c#c$zQD__Gu`ƙjz WvW^z1dna\"YWk(aFh/? -_YxX}sJCD\a|?6p)|k`_W2iƝ?Rcl(|ɸ_$@Jk’.Pzlվhico-U˶ːP@$ٗxgh-,oFB 䯓3ؚ.(@ yixfmx69u=WMOKfHDD1ɒ~#P~;mF_ں5:#X&cg+_xGi9b lj\2"xK]Gڲζш{9 Y$BYYT;#t_/z=jo쬤L%+nItR@yxHo|leh0ּP+o[0o}4l+%݆qr~uJ}kˮŞ X&+ĞH-o1FT䝹$ +BT)nNZ.kS/Zl6S>֤iHi᷃`TI5:?Q?=-~ ĻmMCP^4OMjLs^DC*6uf.W5o^yRQe\qꖯ#pO…ÕRãȥxԩ@*h|K; Pj& n[?팑,F@|=xw%}q#I>i&獶pdWPҒHRP(`9h矃մ<9躏ZkqGk{ N < pA:zkgI-'o` [ƌ܄n-!`pz^j¨@f@ž{[ FͪIFp ?ΗU_hky֭0izik%G66#0*X>fQ>,\E>4={˻y ȿgC#ƻo~6m|KxW_峹ԡUI,eHIW,7gm28V0BQF(ڷ:ơÚ^]_h_m~ -|!fuI4_dwGBѶ>*c_\j:x!f--%>V21lcqTo?D^6LkEYUYĹ@ޤ}`FefPYz:P췬h`?mƛc>Ѭt[2[n>iQH$-\b5/3iZg{mcWVk3PiQ9=7zWKC`瑞iyeφ@Ү,|5=BMκڔdž‹w zڔb ( ( ( ( ( ( ( ( ( ( FjmQ@Woٻ_K>Pm[GHKdg{f,1!!S$O~? x7rhz!f"m0y2RN;ʬjidݺ#>x@<lS x/!5r\ٕS$$sп_.5ux5.W8qNO88:nMx~e*1qtۿ[}i>:n&u;I'vQb.Nx9zm>UWKդk=>AN گ!iQffQGs׶UiȒJP^OڋVkksTbYEGDx_>= [(!=XΣXI V77l$JXnڗ>4O;[]W+.oo*ע,xdG/6"x2{G/:¤[as*+8F|_ΩXh~9NSiڬhͨ'b5tmx/4bNIex4[]+~zWŝ{ڞXYeAsMi}ė6,Ӭ.r÷:{>Hِms%bcuq+޵qIR~Tʸ}B;(/Z}6.c !⽏? 4!֎ }Hzs>(>fVl4px * zW|b #ꭦ0K\}ܴ>0I#pccxL-٬c7e;l¸7ƣxkKM߈<xG}gU&ңO0$q[Adw(O|_;mfIѵ[@ԯRa 6& $#hVRW'ji?| /!ko~DWӅwz[Cmp`,МYI r_R |)P_|m1|yek$y*|<կy>'^֨ KSlu2Xd),y5rWxτ-Oþ15%eW4}cW7d-,ڻ6QEޛxjA-&,* ,J`b+ßo>o|Vi:TWwdLŌ6.p!<}P!x?| [./Mfo+q|㸶6'/:l *կcl%-/GλIXn]e]~Mqn魯I-elo Nh|-DoSBtiDˬCtֳ]A G9D Q&|eҎxI۴Qhn-園 ݀~"Ѧ7k'D }d\,l-sk#_%|$k/ 6=)Z9nLM>9wchcrO?G+o]ƟxZ ;+-#!*9`pcqڇǟ"Q{*m2 *4ޫ~nd }eۤ$(GHi>_x|J|9A-5h&xraVPAkxJ4N֩k Oz]of@z(HaŹ~|Lƭ+<= Z ]^2Cq6H t7d{n>oR]Avcr\=9js|1 {ĺS]j6ht](gbܹJ[ ^:M3[ƛ{Isibk1 +_mVUxYnd}HMNÜ`s%r+uٖPo:Ex?жu L7g8bq+|G Z|P/;GXӿJ',6:cPMU=[nĚMs h $apn[G9\귺fúekxKSXf0L ;#5O~դHY-mw|D`󎸮SXug4՚M9ӳ4d@J> mEFHOxKœ NmK־ڱڬGy7m1P6Ny}E↧_#ڗZC v[aq3LW؊/'q68ZZNj3r\ieظPCT#>ں3xlW6vZƫzׇh oʠh,(ϥ{^6DŽGaCD:݆o,m̒\[iq qņ'nC|GּOu~2[\6߂c;ex_̖P`r;ß|SH&c[HҬ-5=[íD6׿k9LQ 572uO> \VOŽ6|T6m>7‡Gŝ^$59#Em"]4~b($h~>,<# x[ƚχl5=;SݽMxIWx*D`~V%n]hڎ[ۦ6NED; I&]x=֯ke ?|V 0mL f'>6xᯏtծh ¤38X ~-XXt ?[-k\NH wy^Xc@y|p>?FeYKjEӄrGv\4Gͼqо&𽗊|%xzvڍ2,X]!}0we7<U׎' ]و%,V;O-ȏ <8=ُJQKQutK%]Ms#|.@O?+X[84=u{I-EAQoBLa8B3Ś/[+Өk-v'G٦UC(/.tد SCf@xeI214lz7s׼AZ_k/]x{M6jcQ/+' @#Zoiڎ\ZĖfugMM)g;ڏ 3L xwƣjWSiy2J \Z=\c-y/_/x'VcE<&X9.0HObI=&_w>->-*"qFzlA =s׵/ #7CkcnזstLцFM2IFIK)n<'ZRx{ @-q!W9 ƻ ]m걤mwbFl H^O[φ'4в+jPl$(HD Utڸ޾_>(g|Kgb G-*kIӬȁ⺚FPbR6@Ŀ- _MO~?[P[6e$ګRI}^nM ˋJ1jDw#H ё(tO_QKx?־$j^Iȗ6(bYW#5||sƾc kֺn,E%ѷl]ULBؼwex>'ע=yKKlKf#yryqi p0> O[^(׼?3U^ JVf#yp/ث_h0"귞oܭhtS,ei\W*-+~3Lfxi$rgckj"viEP9bO9?~~)j|* ef8d|NBbXbj/SO~]4i/-WѴf#=Y#ƘhF@Osg|$*$AhMH/|m'0[ok~%׉u fp']gOW?d̐ .Ȅz_g 1º弚j],PVՋ/)UNwN2(i1Xl`ׄt[nR['Y#wݴ@ (9'8qg믆G<%ixAukCR{{#myX`p6۰6Y<h b|%mo y{h)b%d|w}1^ke4oOXEo v36 -?rHÆndPWӼIwQsR298dIh+8$>ksxpx[5 Hm$%2T0@`MCwZWu]NKD_RBim <(]]koᯄ3K&hkqK}7|7q~0I¿m4oSizt\F!IWayp\C| -N #AgEI~5|eƺΟ࿅Zl~4{K4mWm :uߜDbZ=ܩ? K9[nch6v($en@yeBz#R56(!xZ.cz}k[y(,*Ut'MҮӼGc w1|6Wou-Nu1?<σAܜtk|f'R+Hl'Pu%1pMaRP}=Lk㽌yd:K/_ ަtNMBfT෰_Oֱ5ޣ} yZAt+曟@6p:ͽ+:O\F ksҵ ] RVvYע1"$Rx8SIB}{z֍e ZaHa=tí ?.)GZQEQEQEQEQER-RmPEPZ(((((((((3REQE&4f(6Z((( R@Q@Q@Q@Q@Q@ PIR@KER`R@f(0)q(iEP_*߇H"ѭ)  ׯ1~l)tXE%,7N|+>:ihqi0In|3BYu"J=:I% '?|7uiW6#1ZOVϫ$҉,>:~j`fw#mf5|&VR5b|Io^xH׮0sDRmW1Žj6PFdg-\]F~Lr ju=ou|9_IXk  PDt˸{YUvmhwPtk8l[ڥR d\ߎ~!EᰶuƏy%՜wڬv #윟boek|%}O_Ğ8hf?E??'<}cVH[mQlހ>ҹ?GA F/b[V"ۏxPi_i1؋b?nݏ3w:WƃkSŚw.|[si0<^m~Zk{u:^Kc<)'[ƟӢM*$XM >t =߽ 4RMQX9dɌ HiJ{x円me{(Kwu,`¸FǗHOR9؋SgUѬ> j8ddm"\vFߞ_!dEݙRKY=0[F@$4K![B.gByDNʩqX?|w+2;}NݘQ#!I$ _F/ >6|6ծfռmyua9XfKhwfn4bX`<A_^4^(m֗U4=cin\.q$q@rͭ[l7HI*D۽ ܹ qg)aW=\KOp_9Zuiܒnj)ךػW׾3X9-NUM0 ڔ9EqN~YE JX;{Y.&=FeX~*/EԢO*{%ۤF_/#u;8{>-jV/]}|7w[>tc*?@GHߜ`_OӾf_YGk[`1voJI/h?h=Wg>tofzH;H20xe/?J?ool0wE!W's#ֹ>0eGiP~(Tø/aenz;}~ |/joxmRZoZD% a嘺 p*O^+u1CO>}UQ-Omp*1rE~knkZ̯ G F/~m82I5g/#&Uwg) J7q+n>YHZё M88ȯVXtqizW٪ku yz0CQQ&֝^W@.`ݣ$wI' mMsrkQGoh$lɃWV; E]d'5^*ֵ7  QpҺ?>w?6:f4AKn"[x%qxc 9wzcKլu:N,nI ͬ$RfVRA-{StbkDxH,Fw[ӴcvcF>U젞3|>Eo:&ύ6u[5}P4{\bU2 |v;Tm#Oҭ56XUW%Hd;Fk[ǿ<;φ7?M'ŗ>OcrBd#Wi#%o Gv Thaܩq]עT׾ W[6T7^E _Ǟտhۏ^h RjmMc `vo:ߵփs4]\]Eo[-Ә2 Qќ3@j_Ť~״~7wܢK#s:? kv'5Ź/e2|,<*6CUW6l,L4|_8已 ,VYIf`Pq\Ig~H|k|>IUo.v06 P؞_swmI֮,e:}s a1*x=}*@]^[Ӵ+7qjwqz(g _#GU_[ZaON1o,Q]nDUlZ>6Gߵo_b<nuCg(4;p[8C}$2 |GhR\lkMEAyoTd]'GFL>iRɕUL`t5f>MB’x e %ȵm(*~AUYEgCx{Ǟ(ҼobԵ(Y&x$; ?zIyoe rVghmVu .[M% /ÿ־;kn ާuw6ʍomm#iTff;?D-|U_][j7QxAʀ@29cL,5pl/o3[OiO&UѾ #*y[s{O7? Gi! e"yP}[%s&hvnxI\HAQ,G8W[|{{YtiZeՅR?/W5k7>-'Qլ)gՋ/+ 5_gÚVgEɽӮ[2IP }X4φ61|Cu+w#VS3d'b1f_.X|ULXFFFȑxoe`:S/~*x3LѬ{hVzUԡH.8"7- _ {W'l[Exr"?'GB|ѼJkv<%k{8|6&j{D9bH1Y\c}/xg6tQ9Ig`M~!_Ö>]#LP(ÐfYٖQH-/x?eZsQW, zE팷 vki aFcLx.i/W񷆴 ]<acuz#MDr"Dd%P9}{iO^xK]C5ijz)m=i|9a%c0}|*pO O@kxSNv[2x/ #JJUՍ[(ۖ8|5EGӣK|XV[9M!!0JpI5]sMu+KM{2+y/iOKuC@-]vYض L" m-mm?z  >EMr5/^ ;ii|?F-T*3* h/Q ";;hW\DQ9p /_qȫߎ|;BW__-mo#K:՘3_xׇvک+D#،q%ƥMX/kqjFt8|?#P$K;!>m hCsڼ1=~UPkh5h:M{iXF٤@vC|85&=?_ǫW/thhvzX8w;NRJ>skV4;_=?lIEbS_ Gg-6+^៊NMWi POjZv]ID:kmXHKdrkT/!4߁Ax OkKhn6m \vU;Y6G@~ZN.49[GQI;b(!'*x xbxr25J_Dnc91zWVzmB]=58Reݤgqp'$#su?5u/V]hX5KMҶec|r~o 'X/^1;[稯K-_ž þ:o|>/$*4NLGS __((S׏ȵ #b|)-+ھ-hjVWHc):ڿPW#Ҹ ĻyO jʻ~֥ᜌt2F`cץ:Qtx̺*v hOpZ< R?ImYl3]jCk 8} { )R[CS.MB|/CaX[0FgV1GRC,~VxcÖ>鰈,laX"EַSK- ^NG]ң;)#Y#pUATP6@B jTz:TE-ZO1[mb"@'jKE P*{mNJ| qQ@'?W-2ў/QqvdidU (.@kL;R97QV()h N7vm7JrxSjR@ ہ[G{KI$!&C!Һ(6J9%ׇ 7E =CN%Z*:23zߢ8߄ 4~|/Iq=G}M,,Hgݎv;ihrƳ43Myk:d6Tc@;䞮MKEVOY$#-=v߅qw4[߉3lŦq>PsoDcGN Ym_ؤ1jA$[ƪJ' [TP'A]'ONKɑ|cB]%Յ[9d@gP|O/cJCP/l?e~d(_˸ccPXt[{(l39?3QO6 ώ5jMҫi*][Ere@0F{՚(i#0Mpp{!*:ndK="}X+[A9]Z]>ͭe[bL.zbc4P=(HU<GZ ;xUS@Tcx^#Ե;j-)O,&xkvd%Y+ ^EcSw<7:Mh-`Qu5zR@ qYwg6a80D(#(XJ F5Exof?V 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-15.11.1/data/images/line_drawing.jpg0000644000175000017500000012767512616075370017222 0ustar micomicoJFIFHExifMM*V^(if%0230&0100Fotoxx:resize| Fotoxx:mashup|tonemap|NE(0 ":Photoshop 3.08BIMresize ZMunich http://ns.adobe.com/xap/1.0/ Germany 8 1080 3136 2 2 0 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?-CQMV8HBUS:ATαy]T Hu=[7ШVG?Pcί\M:AK*")R _?RlkCY+s}iu bji2ֵAkTc[-u}_zHuo_٨bNA*& ]F"aHd> h* m+m̨ {^#[+ j w尹ǶqZxMXCJxSԶ..;KVU[}b0@2U$zƮb u;UM[ hv:ZW}γ.uf?+fun3P]-=fTmoԊr; ܑq2MVU7٢3KV_+nūq".uͣxViw.֨I$qUf/B"nHn͵wIVH8;CxZ|׊# )м\h%`i7 ] xr:} 4u cDL6Y~^g%-N."d%swO}l\ɞ\K]S2,M嘧V;1u} 6.wAz*zې k9N6ZmۚUHEEٯ*1EJr*`koKxvO_yqW|g%Z,h[w5躞&,W͓ [Gk{^/y5]5wڲL5+A^M_ST}pѿvͥL=)ɝƫ8//C 5Kk9 \rxĩsH5jxk6W@6AsJ,F #ƌ?sϲsLNG,\GA@7,~ ?dxW2 h3?P>#ss'h~ @43,$yR2\ǐfGM ]Au AGc irNtms*fryjc#3v^*yA{*׏r6hGaJЌ&NLʅZMh5n[pkRI,Z я,+vXL>%ҿ~]LYj`ӭu[8d͒HM^QfoHϊtT{k [Z֋k{eIƻy[楇\2~<#$s]ZѬMN[f0dbLYWfNQQe M[}VUm8-/sQ>,0rAPb6P/oC&}$[#>㴺X-ji+Q]iPhpnIWW^3}W-aye웘+''a1{6g<7V$~NQQ۹?kP-~#xu}/V|c=^vg٤(*ːߥyஃm,x"f8֥RԼ߈D@;qہ{jlojKg5XhL;IaS)WuXo C"ͳ[T2/̫<#j^'Ɖp/ Ē36Q=H9[77eD$AqeԚ[] zyOA(^x=Vw܌dhe #=ݙP^]A. WP3Կ3y]OULgWUb*$TDTSHaT*6BiM4iO4֤P)G-HcOݦRL4#"EJE0 eȫ >Z(/b?MCǩs0mE(?ʯ@vΧM"4> 'ڳ67u";c6v?ZեM˜r6fܻ Cʷ̑H6\2}fn7Q7Vx7|$~턗6*[B 4zM"ؐmEQV~/ut9>* qu4<fj˒I1C<ݫQkk[fo-Nmk!bB2$ο֣ KUjygE®_z]BЂ$RD3/=KK40\( |WE{kUYw&J~KѨyodo&]j˴v3>W wLۻ,I0dkn~U&\!O*] }5y HT3_ 嶺f>m]+l]ۗksWM Kc~RuU jd_WoKNl"&,mS|mh.uTGe%v.I&ߘnvU|gribx ZB6J&s䍍;VԤ!2$wlݫdb{=B)♋7UQ}is&[*Mq<oK}ʮfҩxGvgn#_|f5Ѭȑ143_@mmu~oP>f'jA$9EhH}  Jݻյ"6??-cF=*RZO Sh}掫Ma;v8*S 4r~i?XnNLLq>];@>n5 )xmԔ-51x]&ф;y_]Ao',J ƪhh7q_ WmUevʮx.n/%[ .&m  ˩}n]dʬUT:}14瑶Gc6F6FhTSpJtev|=k]ZT$26+еYxRHrCS7"H 7OW/U4F~CUDqǻv n{qZZ_lYuN6*b[FZkz +P{|Zg@bvϭGSԴ-QӠDxZRRLM\}xV0&Nv T5%ƙiYa|JAσO[.y~aov_U]Vܻ~sSm&\=e\+F =).u %DA Iwʿ{qn?ާ+"uﯓm~m?ս{Hl%-d㼷]|_֥i؂:˯ީilP۹6yo+æ%'dhee̿zjZ$Ē]Wmmmqtֱ2/:w#7Wv"f]w{rcN?nib["YoՍvp%GW|Gg[}" XEvz$Åۻ?繬' -ֻ 6Kul}Qٻ{_b|7iW/'ڵ!gs1j o-bokhK)A.kuy~"X!?VS}W7Ef^Xϧgt\6V ?BE`ZzGxv-MFnϓe?VOJʚK)!fI`Q{߳U662o9dlm]?e4(4HVc8O (o+VmIG|P=TwjzrH.Z3_۴0I̿Vmюl7noI3|hԮ" ۪}JOUQ$U&]( D)ԛ k lfU w|jklsK-э6ug,F?vUg.a>xSwEjE K I"VSZ=iY;lH^Xvo_>Gk#s*n R]=ă$\Y.!6h~wnoj[bΊy"жSGi:6'k1eiQCsn7l fefw/T Z}ٿ!B*Cnpꮗwdr|/'JWQ"8F#Ve][_sMȶ@řW[Iɟ!,ɟ P,8nۇct 0qI -4ʧOG_ eZ׷x&mMq6/hzGAyOι 3?v-[M|hfg+75XԦjFY u Uzē̻Q37%K7չ/#>N,ofU|6f[Ƒ,1e\E5ƞI D2vHsm?6kӇM8mE]{DQ9-ac|mҬ31<ԤAu+w`Gwm)hmbTm?Qiܭ =F9kiXL֖2u8k6 \GH wb6vZ:>u=pO(}6?;26wSmcFm7d_?\P:i#̸݅xvSZt&4T{wK]>iq"Y]^)n&Ks3 P'gkKۉ٭29+w(X|>%W1+2Y[ ^Y^]vIqnK_OMc5hTΠl#$D[x+{F$ k%(K`;6꿑:s|95R] ]b(6Nu> GsHb&"R̿*ثm*_ߍ2M?%VH[UHfeX]a4\0f6ݭ֩q]YXIyow<^c:.wz뻡}ʊ8uYv,Yet=X7셺~Ty43,G?#U7,[֗0akEnf41C핾^~j{ lϖM;e7-5)_FU(٣ȫDIX^GWOּKn\F 0_$k5rqcY1E$MVX7m5xe+7ʨ빑Wvh;%ek-mE85ϡ6L|es7l=hn۔utrtòhHQM,h12X=٪-ų_wn=nOMKY]N ,.Wb1}~jM`y6I'vG&T񫐷fG 1۵#wqψOTY]IDJwmztxཷPInD_* KMݐ[tt.#divٹd=eT/֛>dͻF-fM}\3i3}ͺK\a隕֤L^XQF~_UkT9ZNeVCƎ|d q,g?R 2C4К+ry@]ܴۿ@ǙZLu!'Z?K̬p mku ]cU[)w] V5G;܈a)-0L*63 D§n  {dTEl)jE cVTcIkmjTbJx?s~6+EAs+RZGb`Ͻ?"{苏gmSU%'PpܿX_zn<vKX:fOjZ5\ɺVj$CF1neuw5_M=[lvl`vAQ.M:vY?ʻ{C uRڝ;3/m}oGT} [ŵxI`Ud.vVX{+W4]O2Gue<qYsSK;|ù*\u)4FgUG6_fz{U5wfb K s*e-ok3!nUK3]][dH%ܤU C'L-kFz-/c=evڧ/K'۩bX$O.uURZo ha3mfH6՛wR+\Hc{u2.%V]w3UQXthcOjvwWr:|#ϴm gHejɪЦ p7#\/&4^TbCƓgfȿnvګO`onq4ʭmf+[]*k^Y,EnwwLv?k_ckU+! d7qى]OhcP#yA [-UʰZϵ\9B>lugV6J'a>fvJpckd։guh,jR8ױZ.Kh]LIJ)+wqPֳYH_?wNHH/@ݹ0|dè<{{ BAki>ʖwя2kZ8 ocm`rPVOڳ-m)-j]Hi'%֮7Q,e!ԫ(uRU5VP ;C_uˍ^|:5fb|F?+mV۵jK[ۚLmBy!8ibFV0m>ZWݠ=~?[)KEjq?ESZ1QIY!ߔ4 _-%6ҎVşuEVO |-+wVr^X}vڵqVZ$OQ[F/3BxZ64MپYOM}Ip*Xx_WԭN^"G ZtؿLϵ0ipd|/7}A=F_kzS:t 3]ZZ5b4 h$5o|lUmJQԧuZI ԖC+`b"Ÿ݉jq`-p \?W+WW_ Civ$Vq7ړr*3UC_#]F2Z}s$ndKvျޭ+rZYXInvy#_OZi֧ Wm٭xA;(l1_EnT5DgC&l(O5> C:m\ݙT.~R=jV~4W׭<6%K*>X5+8dУ{3v5\![j5g3ay -@4G+Q~a{ԚA,/} yr W-E/OXWI?9+oKSd~C娠9vJ'M}$Z*\6sǑO+Vбq/: o7H ;m2{PaZ0+3K}1GBRLHI-#fSRBi ja RφZQ=>6?!PV8*{נh, mL=p*k|Ll0_?: ۚ9%DbDa::ڣZ+JӗWo '0ӷW;Mh?1/Ǜw\ҝ[s6 DŽP : VY[lpluS]G""WzK+4?MR`٬R;S<Ŵv%_ 9Wk0ܻmDz:;v`zyN8ak%3IY-5)̓Ҽ'y6Uk _-zs bo?}xtTkEI{OVyz5iڿynl_, }_jx7d=:vL2xEd6U]uf%_[{ۨĶӶcCj3x[kho]ꖓuDc]ֽ$|L-iom{}>R?磳p?6]]N ӣ2}dbc'5V]74솤ۻ!kVVxiV To~Y6Q+m\7MD|%\2oЪmfxdMmViOͶуwFnU|:ܹ~ZuIJ0DB?*WnHlv] ?#vV1>c0ۼ}j٘a{ܮ7·-0ՅZ8ʎWvYp[b!Fy% [[F_U-[[o v-u@p}ڣS3FՠMM  moQ追~eZ?"~>G߱x?CE-ldJpA۲}n_eZc?jq?3@x\PxcM隼X?d "Y?I4=4yhp-rat^EBn@?X:w֤dL#V|VnZ[LN?MQ;HK8>viRcnZi}_\*}{xfq}c] E%o14ۧٿjnO*2ll;yIުjKAe`y9_Zۚ_ݥ-oz*j[kyfZ,|™-Z.2*7r?Q_3:\)^Ҏ4^cϨxS`,э[gd4|>ݸ񦙧ڥ2tn_(O%rZ|]8u܂D^]&P^<#$֑jߑj挿F mh;no?GAS4'EG?( .?0f+x7Klr/b~ !mt" :4~aa'η?7eiwÅ%ܧkSP)aE }̇oTwsH{&2ZL]g s}W@*+?PJX́8xnw2?/ެ/ŖW;bDWUŸo WЍR]nZ݈+ `|d܍,Y;y ffOS}L?utʰs*?vZuk"XJ$l+2F+oҥ\)\0a!MGFGY[нS:!B#2y̻.8|M Mj7Yœn/n۟z)g3 +6U 1i7_* ;Yj*9;O;Un Zk?n=+E}:;'8Tݲ&?+AZiwijvٞGa3֤j-M);Ņ&gh|\t"FAPn LxS^ bgZ3cTJm:;FHB+l2]1#(jY|y.Ї{~=wi &mOZmX7]Ɓ涑$4[ig{z nnVx-^R]/Vk36NJV}/,ryr [m]Ј[($\T*O(Gna :*ss>2ZrH7FӉI/QW Ro]ݸX W3O)nӻ2věru?s~BmCZ52~},'>Z2Ӳ?էg|eݢ6voVl6/պmL"ui ckl,'R1'c5xv}Rկd2%mUI&b 4SNѬ-c4ډ^D[ݵwm[ 3IW ],6ޠg͊?.bwrunO]Fj%$$1;# rK*$$-Ѿ12>ۛeS]ngآ; 3~k#*wF8[5f]wu ә kt5/Vݏ6mw5 ef`=50IjUf`}jΔZRZ)[x >M{o?{存]ՒBGп@D??Ց_wlZԮ X\ٴk7Уk:5#,<#6;#N?3}I<4OA=7!n/nFv*Oy]ײEh`iX,4"M 3/.?.u-nq5FjVLcݿO2]ȿ2}Jh _ؿOH|?RH,wo<Ӷ ۙZbY}~n@kGCѦֵ64Eu^Qjѧ&Y\>P*ˌ(x9K /v(|18lu(.)KIpWGޓ[AZsaՓnm8Z:~{-k%;{ ғN9qnߦ۰{l/d,lH / kum2.4/I}TKpUAQvn5kW0B)~S^HZ@S$:d@[鼏1$[i|qY4:&tsߋ~GUHDnU9G^NkC RI MX3yQ *xLI~˩_Mm .G[41-45Գ0\M,9%&Tb\Uޝjn0PGI= e&hDؠ`?s>!J陰Y(ڥc7oݽu_W+O$'D 6[{ւmєX(ڮ y"u5!|9mZm)Wy?a,xz8ٲLqw_ˏ*П󖮅5iLxWZ`nrN ,5~lXT,픤gϻkz:\PIyjB?Ao .ZKK.歪OjG)ʀsGESXyXFmYe{O[K 84b1|*(-S_MѢw"[73ϴE{N ii\Kgjr<#[v譽}7m3:a47/mefoaOksf\՚撽S4DF5]_f}?*F[ fO:{]QN$I w 3=jqdoIW ~o,}?j{`fm_ޫ 62#>6cQ?]Ny-M+T[HlhծVz"HMwڧhp-}4旅]ïiwVw;Xߗvt.TK#Y},0?۾WeV_ݩ];is[s$LS"v_8e*-BZJle~y?ǧy$(ە7_ޮ*Ec:iڿ>ZEofCq}ntZLOfY8ݴts{9xlgo͵~%&-MIѿy4s^T:6io4z Qͻ{FuēSuY:;+݋( _RүMDwCZ; {7ykx6׺H0%jYR˩ڐ .?gV4n+y2˹bnV^6wW9˨i{sqnII}Gk(வ`ML'`W2*zK$Jwdŷ], KV+\`.*sk{Sl޿ohv~lvv6 Ψ7w5[}mkcvks$_7wu :h.u$݂5iEX8b=*m yg]Q}Z[-ՅմrKL?Oʼ1暹7dXѴ6'%22LUsIolZV_Vz&V=[4i /oH7;Vn%6$6ڟ>_?w`=zjaQi-lts#Lfqw}>GX\d,˟lץ@u۬k*#qbo q#әѱ{wf5p[u$,3qk[U56Q#sO٭+]hFl0ٍ~X7w E0?7̫׍wRm:VFQ͓a,G2\Y:n_,wJg+ sw.nnʏUH |vmt2xvA]2u$q%aGcQ|r ~iF)+*k߹߸yC[<`˶w ĜWqW tLyk7Jww@ @ o5ԞT4.{|9{Ӂ lo hzo?U%t vA]Ji/?5.FV_rg*]bf~Ϧ'?[?Z`yb"8}oQH?;ǪԞ/pXF?#wI4-/<ڃ/Eܷw= I.xEҲzXnj3ˠM.$;{GL[kKKd&b'Qef=B(Z8bEO,.Mz.h ".?5SNvK4__ʻ}r?G>XivQpf+Zzk>,kfOFVċԼ[qhͽaZӌ~afNn512J;(NlgJMؑAk!oCn@z6uD-iL>]j+96dDUy흻AS"D[;hdeeo b㴋Pg&*xWbN;,PEOz;n(t|{=V?Midmɻ'ԭl߇k5@۔Dv6oHt -y\i$m?UE?#Q;kc -ڝm?DK}{Vt2O7]~P/kK?@H=6}Uմlqmx&߹͜7|7T-0s]Ko藂VsH! L ip_J]w3̠V83:ʪ,5YHP]hAhhǍVlU^^^Uς? uxW.Շ<+0˶cb 3T{vxm5TҸl$ mMef+{Ŀz$cB ;H䑚xUEۿ+}XJ*:EhvFcP[EܻՀ/gyy|f_wjb *0g$o[lȞ]fXb~X5rgWBxڿӲHi|VOeE_oj #*]|T+.&-OV9T3m9qN4uUdmyO xxY5`(C(=?[5x{R㍤,cDhthMyK=ȨՂ%-ZY8̹o+~ʪnqNceu܋0+U{ RIc_IX,Fd8Kٝώݎ,4%e_*M]/7mn67F(%sPv6ꍮǨ5Q#fdf ̿nj 9fMh]4l.$Oǻ{/Un|n]~Vij'ew?/-ZBJ.hB /vk]sɽ[Wf[,J_WEH70h.EYo<wٻv*fj5-6:m?7Q_*UvIP"|ūDe2fM*[vOn[̎ܫ|v5oAx(m&YUyC^ N%a^:prCMI^+-9,۱+/Vm:Gu~7Yo-aM/yȌ_`_ xFmVve5<_^N.XrLH]*_l>w7ޮW-Z9VNH۵? _zѢٶ+FfevHm]c ]TzqnqjLSbF Wv_sDŽwZ^1! i?V+RǕswi;oi0N}ŕڍ"-c;Yvl!R껶]V;{pBz/.B4lŽ)a#mY7UګV 鎛fnlmӌLL(m*O':5-ޥtqqo&オ(/aF.I6kX]\vb#XSθ 7[U39̀xfOw\߹" roTGYvJ?ƛD -Ʃe *gUine49 8kǶQ/"K.g ~_wiϧX`k41Bzܫ}%0y{~쑶ۻuUT;>05o_g=g;n~Bɂ.l}Ys}U-L3+ە?Ѧ Gw?aݷgW5B_ZcH[0u>oiXK)ukyw v%5QK *T}F_jeծuk&kx5bhZhӯEi^q_A'%Mah>j[EF6d2/Tg)co᯳@Zek"y?V=i\<=&+{VuXfEat&oYtieW]mNJ%InX YTF|ߟ]`V_LznH8ᗭ(ZM+$9xZx&u U_۹.ʎ_pnNHdԮ 7a{mhk$>xyZ;Pt !kccTW8dy^[&᫽Ru}EWgKVwJ.$$PyAWqrw uR/챿HVmVi6[ vWwSUJ[N7 ++ݖo⧄ m4ҘI Sȥk+> |y!?'Z@x7OV:|Yd֏V:|-3!OݤɭͻsiM2huwQ{-ILh{ɕڴI>g?zXBYQ|ۙGܥMdu4/WZuomdE$@Όޕ0B,lV|2UΩ3i!ej c eSU S i@5hm e[U~׼&|7Bf=Pe=źT̻wUǖeU}5c-njPtˑ{Տ#nKup6+Q'+[:jav\N%$5<9<涏k--cFoJ/-ﺸZ "e]6i і. , &jTGoGՄz$eR^oD&+8c)?ҌU*[Xvy=4kKX4l|nLZYu_}UY!7Kp=ߺrm/"eQw$Aq .~S&捿_yUįˊFb{pR4OKQ_UvGoݠ-ݯ!umCGKcrv w7u8;K[%oB_x-U7=ѭYj2sK6̙?b/4+KUʯ (zE\SwfO,RO-R*7vBe$z]qu)s)f|Mw}7uq$ҷHY Ytv9K; S|5r~9u?v?LЪv/<(~_zt(YKsBlBZ#m9Y֪[ZY^34 TYU6kX;}D]e#SUƤuW&PavĻP5 f{&2,W'mg]m2hWgObӼ{mo&Q7R"p+Yڹm"lҿDiwl*[oVK,(J%jELZ~, Ϯb#.FȈ?-Ś5տ w$za,}?oG'6rׇ4OXwo⇆9!,E`!x5L;sne;]f[~&Kl {{ެ"gRXdHe~Zm z 6=_O隤76גm]WmǶvy俚Y[ ƻFª_!м&U۷H˻wwTW_o.o$M_+Y}Ʃi,O,|ܵZèNo/I%evk!˲dn]źۛ?N4M35i^^^`K&bF<ml4e_iR$U i$k)?5n9KӅ'vц\(<_jT]wr?:t'BVXVyM)mup}ws{.n'vN&\Զaנ`tMBٚ6Ubp`8q_jvKie8AfO8TW|gJ7p$K`q+D |4wSVϖ+/ &%>n.Wm16x\cmo%QUEZUxPא$KlڵK7ΟZ:{;Df)o2R?>,ޫkXMcqwmpwwՄ&cE%}ķ:޵=l&$`(Q_:wwn+bh5̘}GM ZKh)Ȯ}(DSCo} ޛ4I8]؞٫1x68̚D`g6`~/;nٔak|v҇QKHX\ܨ|1}ڭ4uc[m(o ϴ? AsRHHtW6)V5g-௱ovSVO`9vO ~uOAtr7ʮl}KVj$p$#o4Mq qұ/OSP]>sXۋ@)m58r^KSH+ZZ'fuă&7?q\zޡiu$p3QŔ/-*YpwRV]OAӑM#ο!)٪xwo7xuu͜79`\Fm?JQR^_ieM$^Wn*Օ3cԷ82Nߗ [IFF ST(?;}Q=@.9efw~bM#Xm/ORZi+4ͷ_@OR5+<ؤǺg+=gk%^MRXa8cϽiIE/"dt0-E(xL낖G{K=z~Yޥ.y8U=~͈ٜޟkԞy'j[k4뺸;[yQ??y[6:=klJٙM )bryo Ϣyf1M\#bU\OØqXʾ6{~oք] 9+kG?=[?7 ,fdwFjFL0E2+oN2?u# 7vYqkoXЏh)w^މio;2 |CgyWmA3uU@=54sxM=ae}ÖfZKOMf5͓Va%'I$6I#|5hW7u{o '4ۍ2T6݄O?Q;O֬bB.g;tݐ%]WܦHwj6Daz^ Z쳾 AܭG>nʞ8dwC'O/F*v&tWI]*H ]ڤgzW3 N°읹mWbMmJnzZ~ͫ}RmjSR͂搫*zgsGgG;$wGU+V'mq/VA0ATT+&7+Cݭ[s0 v##)V,[<5hu mۙt޳tWd&1ZTniUI_AjF"6eOoJ m. 'm[xp۰:ba}V@=]ϵKi,-ucgpi֓3)%ڻ{nXnZ_ONuc'ޟę?%/ /-Tr~t6]E"N#++m_2rj'P4KǷ#MI0iWC2jU~~65DC/ޠ|s:o?zCk 6k^@Y჏"=?s'MOm;WHJϓ/_wk,l8B9V<ʤ\IIN{&HܰFýv<*ۼ+ ZKŘA jGC㌟]Ƣ%b+FJĪeG`0WyVɺ90JBq?ҮgU۲,zf-$Sls$j5"hE*u[]G3(w\znh:]V^,6f8\3< \qk֖13{&߶C`%]uUzL>I+I5I+Ÿȇ ÷ zjR:M-" Č%Cp W]tUרXg—ڴUhV mm;@;k&[YWESVkW9?wo^9tze.u7zi55DaXOq[YN>1{dyapPӫ bO*w_*^@]Rpoh|c!MI}Vzs,בosrr#Pc0I٦tkЬf@z5s5(UXT`p=? WƬ_~TЖ ~=Q9c=*Ph"k}ާ`v5wMuiߕevKVi_i*VEHӼ7suʟZl-Q~'ʵ͔]ͷszy6-`j,Pis/~m-U<]UWޢYb&/E&~*W=ksjK*]\4ӶU;nHA_?MUĻV5TLܛUELwj``|D2ʸ*$\oj"nmv/ʕa i_0s\H.[ UTAo}2jR[j?6 Z #N܊1\7n.d_uj*gvڅk6q-w63)wZV_&8u>ôn Ik_V9i&C Wv7ceͤvQGb:TJ7ok)9w:CM)w~ nӮkXVlռE+LF?:4c(Z1gmp9n$"{q}iԜ,cqdJ6kNuO[i7})&2n8l(Ug߸4. 9Ѕȸ_d 1]#'nu4.a a@N3z-?\ 55n+(ҢĈ=6®2NĸOKH.MEKÌdY3Mkq$M!0kh[CN5؉xEkp|]#r fMgFAxeVtMh_'Doڐ?w..%¯h۟4{|ip%u[kK#[VhsJ@?2b,lȭ|QmSDd)ehΟ%% *v;6Zll.rFSQ[%t6 tM37OP(YIBYwչi:뽶cyydZ[\wpi֨M5Էw16;i?jMfmH?}}ʋw (6'COUnʱ>iVo$ \c@'MJ#Ho㢇r#ڣ ZinV!$w<j[4I@v/Vޑ+J/HGp;JhźǬ߯qe2}NˍgSkkw 1{ ^nuv7]$C]N|F_!Ѯ~W7LRpT̑!)ӈJPm܎@l6PF<_XٸT7Eh}EgYnP~NToOVpjwװmXn[1e?餈HDߺ --|c>TEej-]BVM77,y?}~0=>_zBc_}C[Zqcwv[(SPԐsq}迾TcdH܋Kmknw_ -=H^+Z?2Ҿ39cd1TI>7wD6ű@msi>U"d XY3Y~?yD?2* @Y6$=_Z@br6VYC}yut`WO:10JbhzDeid`D@n H {?j^'D6 W-g^D|kǐ3*ZFἇO2`7=y{h8}Nc/j?.O,߸Q>Կ9O,߸?<V~G?*9g9/sY+Tqtye@SOʏ~TrϸsS_?<V~Z13*=G,5?No/j?.O,߸Dw&G,5?Nc/j?.O,߸QʎYj'_ ?]Y+Tqu9O,߸h<@Ӹ8BپBIOOlwsʠg'r=;In$ܶ򢪑1Wn8o 2]jlf+dᎮ |gt~ȁOޯ>)d9F d0yӼ7q1^3[?M'$&̈ Ugo+ $xG(mZYB#Dŷ7Ua&U_ʻU0KrAVE BUB;u݉#'bVxdWCnB_)mY獷UnR)_WwɓɄ}fvNKcgJڳ)O~pO"Gyϙ;i|V`MFuFO42x-VOHq\!UNݭ~-s?_ڦ䎫5cd-7 &H]Ƴ'G#?)O{_@ox\b1vL]ϩnO՝򥬐Gp̅y+K T ߻Dc.RyP2,s~U*n$Z4n$_3iR\K`FRΣi~%k]h&B./!v銳mz63mv% >W/R\P' J-~7+woO,e;a~rR1M4%vhCg[ V\W2J?NmO\6𽶍 uK2~}kn8iv?u!r,-n'e u}lGmh ?MeƏ+YU&m/&IP[;eDY{xݹq^ƓIjƮG0+5YHm34R\nճxfqu z ,V?ª1TG.b>/]Oar+آER5l3>_ v,w;mjKX\y8,#<#g7WL,1 eiBmѝˑ%܉ߗ5H>Pz66[?/KfLh yMsR0םۿZbuF%{U[{{[^Wנ_|wֹ%̦ *n2g_^1yہn<{wp[kXےK+dAo薗k6&7 3Af0g7*8kkz\P5Jak) #SȪ{ټEg.`K2B1Ge2ksy͂:cւD-l2(sy z))+frh~]h~]u8>qU#kWGG=94r>OO:dۀOG}WGGG=Trh]jSjpX=zLkX6F~"1Re;YwIwt֭x B_Kb8*/@)5Dk?Ə|4`|&#j 4G&#h_F Q_?k?Ư`у@Ǎ_|^,-Uhbm?x5zw/ߘZYP}׭xElҤܗF>[/X]M lO@7vo-m7~z{ߴHfY"udѯtTJ60ȭEmlWkmngf}j0r"N-$Dϸ?d:IUϡ?*ڣquux`?x5Q#D_ݭTLrWO:Mtv_Vi0S}T,mh_sG|Wƿ{bvڥ} `_!Vg?i?LC?! S\/V$Xu\l 4~ָL u+GMi}m I嶏N32UB}v@_hon /P8z ;an_L} <~ ۾O(ڿj`[wV8Um_ޠdr&&o[Rʰ۹Uޤr."UfDr3g4ڻU(0ݰnw6wRF_29_ Y~Z^=jX ǚ‡e_1 nHw껿ޤly_YhGI q=Ma_u _ʹX+@xdm ^]hwOqj*T},@rPjfniͣY=q;~7n3NQs {fSn}?3m-{!VNK_3Io#?BӃ\b9 [$o_kt|#J_xdx?GOW嶗}kNb4{ JH?^>R?ﰲN{qyeR#Hp40SnﺿӔ]T֩l 'л=OEl6:L7u{Ռod/Md? iz~u ~w~>m&iji}y tmP]~Vmn*m@Ecsoso28dB#{yiWmcodk#EYmlzOUz/4{nGܹû,[)-|'\o椓vd32"y:\[en;kVqn,K/ɶe>?*}jpYn~xpGчt~kG#ndAZi#fOzcg6{W?>7T%A1#c]Wvw}I#+5ŷr_ٛcz߹i2x_S@5U1?7KJvZ 6o˹ic|~D#(۷re_McoTc7@]l6֤ZpMʿ´3+|v߻ ew6rof܍*X6;hrHv_@ϟ'_Wty_^g5El)Ϝp.0r3Y^OUmr+G iuEEG2I+ #מy;!K2RQjc#v3v`wfu}sD7Og e]rG&|}ٖ[ti2J M߲\Zo2O[jb@x'u7{W2 v|pG(NEԍ{7`!Gi8{wt{h6fU>T? &/WCqu(1}j$Z6YIc`2Ef5UdBdҬVIa][%{ZXtBnIgdIYK)?GR*O7QV&!QqQ5эy`3)?GR*.#}8ރޝo<7qy,d ~`oUK?w?TSO D&c2{oUK?w?pƯ9C >6Js)?WqER_QsR_ReGxw5 U{HvbMy-##mnmfme,gZxu֧=ڔ0AQ%r;_,-t4?/ Ӗ=. va{5'+l;  7~mJPSW?SBji8~c}V?-&v {妄ݹ4/FFI1*b HwT@vZbwQ |vaQBӺi 1T8 P"M?5v?4 ?󖸖'ZMg'FZ8MĬwei2_y(#mԨSv߸w|JK֠bǴRXi}/NĻ]vH+6ߚUCUieoMnQ@;Yxz2\*a7|c 0Gîh6@ͻnjؿ*#dC24/W[P1 R=>w2Ÿ67J>i5bI;#chۯbê K),C)h$k*9otR#,?gG񺲯˻z\u,-OORXC Enx}޷tmnA 6+?cNU*+ֵVڰmvş~ ik (w7'xKo0UFB*/\ݞuiamŏ~7,W/&%'tq}vE i 0~\ME- X$(1==SUij#.ʋ;|NtaweT!귧3jԯi]BI62;Y`?Ԁ˹Ry7*+6K!i^U*aoݠs9mw0m?42ojU,6ߙi eFʅw3}ڕ. .A5lhe 3nRV[hyWg 0%]DQomjHn~oeUMO~ߛ$/}_>l>&J )?|G\ѱWAspe`[pW56HΧ9/c+"*2dnDk #t29$ݡq y%yW7{˪DmU[gYK1wӓ]ArqsR?oʀ8k2Pd&U$:lzq*ׄ5 T n}7 a]~ߕrmkՖ n?SUړAu3!]S%=;yTo?oʀ9<q$gͿٖ[‘b%@~?uMkn2E+(W "Hǥvy~Ty~TRg;Q7OAI{5Ŕ2H@CR#?A?_>Heyw#ρYy!<Ͳ2]<>o?oʍ7@%{ Kpy>u*HoP6჎.xmAahM>r\$$6qWqCKq@}׃oF'o7km }*şomVWz~_kIjGlZ ?SjGM75i~i?HjO4tymz~U-z(K6ƛ ?S5OE\2{m2ޯKYƐԻk9Sy;FJ4JCƏQ}r¹歷wʿ6ˆUձ CToxmrgK}).O+vx|fRR9tȔy"v|VqV]Xg'~S1*Q #2!|+yNۿj+MR_UẈ7Rw$a)XwZg6R/_sn?X+]nRJX6˵k?E0xA2e4| ?çD"]^G*׮†l_)U9єxZbvݬ35 P~VV!BTzJhfc<]WU_i2w^H?( %(GI6ʽ _^ ]Tf4~Q>Tfotoxx-15.11.1/data/images/G-menu.jpg0000664000175000017500000013635112616075370015701 0ustar micomicoJFIF@ExifMM*V^(if%0230FȠ01002015:06:09 22:43:19Fotoxx:trim_rotate|trim_rotate| Fotoxx:resize|tonemap| Fotoxx:resize|NEhttp://ns.adobe.com/xap/1.0/ 8 3000 1000 2 2 0 C     C    " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?φfHBFGrZ3 ڽpc 7f>><9e-ۀ I63{? gPq.%i¼n.8h&^WGL5'H;^I6¹;gC[invWǟxޢڇڎxsOr9 O=O1t  .ȅC+kfվԆ;~|:d14T֩v3)#:3{iVK ; Oz+(Μdj%i4EF ;ryV6G/u}VQ~ʅ_-lI`ьS׭&'6y w.g$(S=+AVe_G{ G@x<0<;xEuD7fњ1 3 l`OܿZM@ zv۞q#9րo&?*~*|PҾxe5V 庆 [٧VڈMas㾏>Ӯ4cD)uh.渇 mjWzݳ|L[{C5`R8\2:֥%ά*lj|7Ư-3MamȵlGA_+xz?/~8r]`gp+- K{Ewimi Kh!XP[N[=~|)c/# Lw>)AbfUw)G4R-Jŝ^ڼ_|7%ѽ[8<8'8_ZҾ ,ڔS4Y7xVҼ _\ %a14C !~Oǯfh! {'EgZ/?"=Zf3yְp#F5> m$I];:$ZAa/ \,Ot:d `pJ @txLekd浧T2E Iof7;nn3\Uye}o~<گ> q.\[ۘMEhתY ,T)<~EId} iz~Xj񥅎\)m8 [n +'֞#l|?[J 审2H>[F_q1 + Bi~m4m9YD#@ǩY wgLklR[+  Z;.uOiֺuլrI{O>vfU8^35<<7uk{k[a3@loF 8W۾'54k-YB1OK4(ufw 2i3+>U^ |Oo.$̉maCW`wOe'tWmgɬj+Ky.eHʊ. yPУ f:EEן!1dW7?_s]]?VSK4҇߿8#|7WҵO_v$񈱖Bƶf'gjO|es='B񝇋f5sd$N6FGMm%J럱j,n{I5 b>u7 `7NcWhx*Q iR2uC0`0v:ǭx/Ş|cHY% [<# zKcE_ee,J噶P'7hp%7q΁H#V,X4{(x PQ;#7Kz?g-WKRkSKi|WHq:HQdz/ڏǷIs5tv1}gge'-ծ5M:f\ڤn3k:WuM>P[is#~G C@oMCúN{ck6yr\n(Hy>qI*kkfgZ:%sA?*.EEͽ]H:ς<5m1&%gD۰gX_~):ad_9=Ӝ<ׇYI-WC)SRJ7[s_߇~]RE<&-S>#z? G[↑C3Ȍx1(~^wGxc_;[U5$ 8|; uw_5ڦڽƴ'U$|ɑ9reX9NfwVoTQ^Mk7dxwDk;ylmǗ8 :ҼmxkRH#תG},U۹>!ǾK5{u-^&x )f,: ՇϊxHPu[t̳52Fw!%88GmWC4_xSĺ/z#$N )/*==;h: {]c]+N+P " F*yEO]CM gY% +o>X犊xGO84YO2O..Lo9UlW:;wZ{qϨe{1|(͜t_G| s|^#H[{u4LYANk^xNїTA.f"58*߇߉}?ƾu} N-K <=sKn{qF‚ycCᗍ|as.⫙gS,*"(0`zx7&Gd8| ;K=/Lo]izuի-w6ɞ2+յoI'5fѼw 6i K6ur.mg{\)>>khhK - T . =o_u/!bOMed8g H8z栟[}JK?V<}FtLD8ںO|!Ksqao$yiر,@Noٓlx3Nzf#>:UjL呤y$U #'㚏ߊ~"xoz4ť]neug)B噀\,JrH<^QxQW3IԵM'Tm"8om dyy#@`Hxo?xZ;j:m{}\ 2*0'A4u^t]fY>o_N簎9GǓ1,69[?ZjZߋ|Kq&qjio78bB}s[ozƟ}y 4NPm\ =ZKlmlf5:[yrH IdeђF_t3Ll5cWԴCV;akA#i=Wl5/M7Ĉcnln[|N܇ҽ7_Kšֿkzs{yBpw5PUp+~G×Zi Xy"OIYڥyP_K➥⟀-o > uw2 8 d3h~#xeˣVv,bhŲ-Hf80c:|~>>=խܥՌ\VF >GzO&oϧi5΅l78  G47Z}:k]rPЯ/'u1y 8 "8V@ݎ9xC >j. 0 K,j<8?W|P|Tf_1-(Ǵ\wlswj^+\iO~R% ꠓGJJ( ( ( ( ( ( ( (u~#EQEiW'oiÃ%cӾ;0M1bxxjٳu>;߇ eF^CgtBJ+b BqKF3@ `Q@BsKAEPEPEPEPEPEPEPEP֔u~( (>mu, FFjGd<f(>m]u[-f,@-8:ҥ z*Y4ˣ݊U?k}P%䗖 / To_+eBϑj8Yl.M6S_ZQ \rp;= ^c[K=2ɵqX 9r\'߉מ6]N]ٍOw`Zt z?MFipDP˞vpy`EMz)Q,*o=8F^٩oE&8Ke2k\?`9ۏ_]jK~Usξ*|eK6.&8Vs>Obohzx/ڮ[iU ]UL=V>~;f5}Mҭ<5뷲-nTxa. N@k3i}ͯ]jq]-!X6[v0A5OO|+際q/BVv0.N93U{Ock-CE4jZF]p2! ^ݴ_j,ޡ+2!$ű(ix$\s5~̟ XP<+xBYYct/ 5Drww}漾W{mGZt[ t':?uH8$@&' Aa@yWMu鋧j>$iڼB;7ea0$p}+l?zPծ|Th].U4sc*wz0| >)G_kvZekv.{o1*np漳7?[Z։P=CY<;7%k:=۫y,HĈ 9 U'0hojڭ֖komexI8-Wx/~ᇋ,;^1-HGR+Ehђ2 hp x]"}R]-N~ \6IbܑlAZwOZjIh|.!2 ),Zgo^d7gLjn}RK-׏Z7[P񆿣񷁴=~u֡Mu9o,q|8k]Ki'n593E]Ew@v_yZ,Coi"xf.+@ #bk[Kr+x;u=E1O T~ ZBo=n:wll.u$hdRQ: +o-7K-SM':<˨-#dxoyKxkUǀ邏]ƭn$m ,៕Ҿpg]sW?-? C m&f@k@ ((((((((GZGJ:?ZmQ@Q@wZ:uͅ)sir9cq|_ٟ~^ c[ܖ KedROBj NI=dȨК襉ٜX-,NNt7W'^I{Kg=+蟃?׉k+ q hO%>µOk~Ӽ;cRݧP3?)#=#P $N%T T}l90,=*WmF۵$?O]qƨʸ R4ri0{UvqE78q@LJ~խu; mFՎLq,ORzEiV$GU?((@ȥ&-fP[6[y.-IHm7.zO#֭R`у@ FqIFz2(ȣmhȠѶ)h@ )h@ @Q@Q@Q@Q@ :?ZQ֑h(_~589~Fڤ>k?P|Kt.f>)yY>uyG@89"TSM2Lx5\'?'CЊyfX1.D+rY6088k:XZJnnk)PF3w>Дq]q.F8]jov}2u^7ާeYo/鹈/hZ6i:T.LHHSk<jvZDk<#~ݢH̊ \ |[_SY< { bmCIQړdİhٺ :w8;¾#𞉨kvi{-0Rb =溽kZo,VҬ/gXbR{b|!ú.tZm>QEQJC-fd:Wϡ~~<չԣ7u7_eych)aOzӧdubD1lA#ֹw<'yd~);he j_τmoiiψeʰ e~À/u YȗKi'S%YBIRD6|ҵ/ ״K=Ξ?gSaK “ |;c >M<:ׇdKw$.Nm[x1Up@$V\~|iGJ"Pg- jRrH  hƚTP~p @u;ۛZ^i}} JtkMnXwO/Hf|9:U^ >OG"l19Oˎ߈^7ŭ;z=l6i'XhOa9`~^pwz)-9n%-$8D@:OVVvvZyis/ื_ھx~ xM5dԴ-sJĪ32")#^+wZ՗r|c]k:֟jLDs.T2@o]xEhu}>XdHkF+x'UứaiR1AG{3ݠ79:&~1Ǘ:uڿZ58%$HH,8#<ק\|"o-!ԴdUt 8;FG">ցWZuX/#ya]WEGo7Zfێwt_^o/ť&i5=;GhspX2,H^~ῇ~gḭuu$>7< ?j `P{W*rInPOPAÿǨxHҟ"mZP)>q'kʿbqZ(+MwX(rs`+xoOZ~ ĞR׈S#YxpFdwm|oMִۍIW26vA1IxszG,yުI_W5~"m-Ww,rA<|\5[L]xZ_ Zvo_ BS`E伾ik:?~R}9-[茫) 5Яk}gNXnO6z`܊u6:XxgMUZ-yϕ*.h,ZPHNG#>x^Vq#kK>i٬Nakˢ-cۓ>>Ѷ/5CaekGoiyç"F\'qz*_/Au0(FCuX +𵖹;<:Dژ_@9<M$Zevϗl3p +~Ts%o+!PHflY6A`k%8>|YT>9/k9 u?Fjw;%cXX5v J x⹉eFZQFu<~eY,/h241y4H*^ɤaqZ6,R,Hr Tާ jʞ[FqEOE=JhRe0(`yN7E8M-p  %m. WSo~ݨjZu")8* Ԛ3JFM(K sIdB0UA(8cF`)ܓ@ T{S'&hX۪H ;vBK2Lҧݑ_SP%pUe#֣[ d6o# aN=ĉ"QQ`*=E-!4vpE#1#s[Kq@m CIr}M?hE:h:En֑uT$'kB_ ˾NJ4b]khfynWrP\e[XRgaeq( Gh 9pZAj`8TFA'⥦у@IKF %4`QKF %4`:?Zp_6((YqQwv9$2] ^K]6c`HF1U~߿5tKM~ses?.u2 pڢb?|.<{!'*¥jXlKw%}.x<9]@wR:i:Z|.F_]~?!oY$-l(Ǹǫ֚xm. ` ?{E-!.=졧L}n8n,rk3b<5V%{.1zg\fCj(aR}Sw>;AetnQ}B}>(^VFL['2xѼq/A Kn}Myt[d[9zp)HcF_ xw5?C&sq[Zhs@cB"pOzw<{*#_|Cԯ% [77d8`8(}Kbվ0^#<_wHC;m|¨~vT4"v8-&Ⰾ;8c2g8D>/c=fjBrXܤVRҾƞ*׭?x]OBv HMEa4Ď2>x^ Sx.cs,v `.~_O 5ć]Zi8 qL5Qnj~&Vi?l4ko%{+FYD_ch@woy>Zž$mg?j'NtIG[kw*DT*H״^9CcWwĚp,FXnUU$Qq2Gck1Ѽ7yjwYjRpfuRW>K{k۝^%{-4O4T8;C`W4گ5-7"ӖS F0<9}3-]tḏg|YxHTotH-8V_o9Kh#a|--ƙon&.gɾ2Z@*2[y_C{?&L5}.}V |1d@wxN}jq0̧ j2}O?kƫemm!Kd"Bp6|x>D񿉼R5Kh'$a[ !)$?B,u5++{iƲ"d# b`}kKx2l:ith'Ƭ7f|Mj8(j>#[/feNp4Qf>u5~2Ь=CKK')"5wxp^9vi#ÂAzP:u%̍qxʧ8$uU6:cu_7G&]/9-XG CfA⯈Ɣumx?V#!׶4F4_7FʌKxxc9/nrXŅr=2+/w˭c -W5s KHq{rfDuUrG$@n/&_6}%?X,z~.3_^9߇u ojx.[񎒈iɨJ݈3981z֧^=^|LԴ+W'\\wDžQv@X~|J'XTiO,E}E >Jd;cvHU9 x_o~!]iNP̰@V^)(PH˒l<=i<ьtό/}Wɯu[Ɛiw$.ss2dcڣ𗌾&'<&y%Frصaw˅à>?jZv "Iq1©fYkHzW{Nڏ -G"L`=EbXH&1_ƥPg:((ÿ!?Ih6s2áy_c)xnQФ}Z?rAcn=+.1+/Ş YY.<@ 859n'UA"#^P=1i8enN%&XR%W I$Q@2:Wo\fIһ:9Wz.ėv]cGIGWr~x2m=m55cF#fC!8]<ғ`xM F[.Nb1oܦ(9#+wa𝇆IGԗV1[)>sk4}@TKt?-mc&X,|7̆0P$;~)NjuY,|=kW[MN]F R Ʋ!Bwfͤx)|<|O>s]n;R6X&Iv?>?4-?\|Xݥť= I Mv8R{j^\enmb1ˎ[ m5HT:ե12Ac0sGa^ӑFExΣ#|8uBM;KQi+ۭ Tҷi^k~L 3{WyKOy^lQԥ[kt8u (GPMs쿠XxfIrk\< !mJ+۳L@wį>얗I{kqi;=+"TpzX^< Tkm:{k{u$4~QIgzp R_wíbDt۽4co6}-lt-j+-.L"c )Bki)d2I3)bN 8^͑FhgA ~ a)jb rNNIMy!9`J%5-Jkmk đCF8^֐P!|$^^iIs6ͥJ$dwTk}2xᯈ&[8Kr11v"0p+ղ(3@Q? h-yE4 }׷J6\wEFcݤ`Npȣwd ߈W&b-Bkqgu%90ޣ$`&oᶧeZŦQiJw[4%(@X6q^֌@C_&ŕzziK-h l7(}kK, x=2ߓĬǴ kȣ"<٧p^7l>4"Hኆ8cZ|A#xgM*t6Kc466S^%g{C1D?3tdp 2(tᾗג}P:򽵥rH- $u|} ǥѬ5[{q#|>sM9bqӚr(,(uᖹ^_]7m RdX`x6s״28ɦEPEPmޯY\Mi#%婸T;䢍{k?$O cPCs2,J:1vHxu>GĐ s-sJzZ]nGo᫫g8RxQ~5xM #HaSU1QAڸow~=j C8Z#?w _XwF͚RJGc%vx|mcWg^z;=h=k>>6浺꺜zuqw:\hm[񾧫?jMmi҂0)'pTԛ<(#__Ծ/x\ag6V 9ECUJ i x?? ~6jX]>K,*w;Nр}s]^>s)h9q_+~:mk|%hIgkެVT)3Gk5uc)|#]sUEh۔` y}M=k&k5oLGԮ󥱊R@'n33ֺhQEQEQEQEQE})iPQEQE3,wqxBse#xMA֠'?Vk{=ܺwO3NէӣX`N pzu?^~+{!XQ겴jwvp?Fk>.oYie>Dyoup OOC׫^eC~6d@Sf"#Vnqz階mMGI{[%-&QBf@9`s|+ WݾǛ,."Sge_o^-s mGgkcw\#ov5ҳw=ȦM?_x>-ޓ]Mmެʰ(0=ٿ>hB󎔱i[ I-Dqႀ$Ws.VQ7ex0ԵN$[K>,~ L ><נ|S0_q_QHWVq&Yǘ n8Y3@&[MՏ3\񶙪|=dHt$e-Hz%@oGkQvsDc ƣ4QLgܶͫ&ܰ!}Oލf>]X|q2ORbPx->Cld+JF@M]vN񍇉uF3Lu(d )|Ѻ<Ol|H#k?.+`nOq+심kuY&:욿bX{8mgC8͏zOC>tSD'41 o:ݵ;J)5Vuv_ѵ}Oo";B+!ȯ03֝@(|@|kmc]ݨa\FF0A2' 6k]~7$>iZ,olq]Pz|z6~f+kso\xvݧCʃ/etif/^<_qsf^DH0R$ n)s_Rӂjn<^Zmih,B񩷒7@p|84g4>nXx"ZK'KK!`[cpJh|R,~hZii[=4J,S( :ŵ1__aw?tGCIH֑@bI>keҾ!I s`OJпe/|&t.Ӭ{LdCNd_Umhڀ>`?e(~՛L2>mO⛤~:%e{G}ks[?\|L+1_P Pxoנ犭MgŞЧ͐,A$Frwo Tc/Ǻ'mRq?>0OWT_Xh)a, ]0oEQERQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@#JZG>QEQEx{ynYx`:G 'o0kLa[[R@BTA8 xe@eAuǦiW: xLW9lW:Ewr]ω>|yunՙ<6R:ڼRgXujR tKb9-Xш:W_\nΦҳ7GΟwUQk:{'="n5Z^=c[ Zv݋ >,p) v9\5c8AABu@|m'5 rFQh׫$<$]I +/-|Ah&>CHV2L C|NKG(Y FR O Ծw/W4ش##L脳;Ėc$Wc>"Co]^xL]mmn DAp('[GğǍ4OR&Žny ^ ¤{Rڀ>]uMc/?/ YX5% <;[AS^yMhG&Y;UᙲG.X_qdm9!Nw,߆.-$? mu2ƭNҾ|ܶ-ﰼ" Dckφ&ҴoMf-1A;i#UF#JdbhO i4M_PswmZj1]hPi\Y4k1b<̕MT׼PE2{K]_+$eJ޿Abc#?e|4ڵsᶶK-o,:vs0߾)QEQEpJvR.Cg㚆y⳶f KW6q,\K4N?uhr;J3LކUjp=H O~5gJmB֮9 ~;9tBC,~ueMj{&[;]Bi]q+_eԠ4#3riw@Mߌ{W7kio4gˍ phlJZߴ/%>տq)過x'IFҐ㓊ߍGmKojmm,0w9{Q \ 9or~#<4uO/\YGeh7!4Q3r(2@9|nm|YzTu93qi%|pVTpkwO.`s79JVx⮑h}OW02A9 95hD{X!Yrg )vEzD u{X"Gðm?0?QEQEx{-߼Yd7E ^)? 6r#Bõrum. {FӮ0a}:z[Eծ+p#فF,Z"N\燧oڍe=X?`##?Ҿ0\+ cxX>#/2(c1׮^+֡;t-eFid=d`kPg5v'9h{uu#5}NNQ&-BS)5U\H-9̓ڼg?-ܚφAvW̆id=,TF'knZS? /*+/xwN/GnoU81Si>ŀ /Nρ hmb dXH~A\2IW^&㟄֒u:/{W}}[)15 m;@_|']MKK)˂V,ZD]V%ؖX穮^zka2kJ+xED F\_Uj|6dq1FpJ f;v]w4{Zgm~G]fkfF͌C|PDlrp6qk<$U J%@ $铜VW~ nax7C {m>%nG`xᆑῄF}CSԭ}>},O$(ŗ8ھ- BMǢHאi~.ⱽt l _ۤQqD@T (ڹ -^м3i/$Mkg4f\yH]~\7g\w.>OF?s;/g&;Mnֶ1G]jTm1P7G!\ q\ȶ?k3nB)h--%i6MX mbHTv*~=Q[ag^k7F9};g5o?Eo nܖ66$Ic^O|mruU{]o[>,񶵹ԜGonu[[*Tņ}I!rjܟ[iU31UH&' \%j;B6a}KZ?)ݸW-݌1~ FԹM9]Zdjh= C{+4F0{է3_ SIϳ]K&aQ!{yIx_@WÖ *7F7K(,IGXռ9C]XGHܣO(>'i'?Ko4t(Y4I%9лG 2Hy }CGφ^֯,`X籠jFM1B<ƨ5_?X+/iZ$"z;ֽ>IpO +DBgGO3_ G*xwČH Ϟ#}S@׵-fv?ˉ-4KjZ O#";|m_b2(幷IU8 8 uk+ӮΝg%Νy1ƼpBo"V8 p=:5@_egүncmZ$Z,>L.=T~^*ĉ&b]IaQ8 {UC:^a}{YdI$b0Z6#(HOK>x/J'@-uI,ۋ+\.Z!3)#gŚk 9]z-vOkF4M/YWO{ M"啣hA3_s?|5+MRT[V;DqunGπ ;U}-᩼9x_J ,[yuޘg47-s~$߆jVgi }?3\Kl=)lz ~H/oblmip$#^Vʍ(RQEQEQEQEQEQER?Q~@ EPEPW9P1k N3X?_S-J]..$ܽq5 me+z JQyq=B1cKc[F`rv$~)FKcc@Wg\f!4Ϋ H8u0 AoΐH /4Lv )-ĢA O5w#!qo=?*u!84f($ȥA{Td)S_:! lu*x5+kZ(gYdI8@ "u(j7Q2(-Fh4dQ\n>#)m& ,s %KFARƓ9-ӊ~SiQEQEQEQEQEQER?Q~@ EPEPO"4BzIW)xpgrjҼ= g Zʪ.{_Z=k}we ,#{!cwgӏSZMmt_P-! sQ)}nokӡRCއ߈е?KHd y[KW<{Uz_ilX$bݬ #jMv(ͤW@k1ArF~lzt>Ś" m}/L#?c >R?wϥcsQGvG7@SCzbz`x'?_ E=6{k + )#q85>#BO)] EN43 Ē+nvI rk־)'槣??jfX&dF2 pv_, xW߯Niu(t&]Lq@5#OŚÒ*zb< ?f/Qȼ0vwjvIuxm$әETmM!zku]^[i-j!Yoig[`2=kG"^'uOmC+!fŜhUHf1[wEίswnӭ`pYK%'8<_TS[C_NcWA/'>.am|/ okuck#2pק2mY'/sEW֭{u~e%QRKmV vR=jedauݵȑB6U #bG9?пh|>EO y4ˋ;kYm~r60p`W/V{xSLtB$€\HN|0=k_̚ x{Pڜ7vp[,##ܳ@H$+о |/Mo :f2A39=HM|G_ށVm3Cɒ@ٸ3^OG~0| &b tV,w|`đvz-?u_W/&Y( ]^fqRSx><|Kfmg{"4TqrWe\ 3_|h.^5Ht{IkKE%* *20sJ>7ִckӬ0K-jd1GtEV;2:h/ﮭg: R܌n3#<ֹwJu+OOgc6̢[ `As@f~,|K[-ӡ_{hW"C |oBY !/>(ed޲bC7^ucXNwVk౤qDPrI duOBռ17C[KB7BqcuUxZ{k~Hd鳸GUfZ=2x57O'⶙Žob1YmFg%i",##,n v>$\~g_GM fTȕvñI\q]#~M|}k7_[jWAUYLvt`eW'I#K#/օeiIF-湼KQ {@۔)!NH~/|EV:wa^p½p[, 7 !~-{}͢C\SŒ5#.|RY|ݞ]ìM4H$Q !Q'㿍_ѭM/6z^\rIzs m`{qֺ_~̧'xY𝟉H?OX;ͩVu&7)ScঅZƧPs1co|y'q!B>1|>}MOYu v[p2G i (bN8? >1x |=[k𞡨jzڌLvW%_P|J'mkz|DGNUX#,FbHapuSޑ\_K759.F͆QߑҀ8h~Ͼ)ִluk-ZmWKR5+!*IOǞ+<7E[5: oܢØ_n05^mkHlYN<%}{5\5ŵ s㚱lx[S-ǃ4k־Ӽ1H!rYUP%Ptq@’ 6 ( ( ( ( ( ( G>}(((KKuPP4nBca،cUm(`(P`~n{_B#o^iiW+ޕE]401Uʟrԫoo.݃4Xw<{D[*NcNԨ`=sמgj ў>Y&ԵGZΤƸmӽvU){Sf]"Tu],4}Ϻ ˰\F2I\h/XcxQYBqubIBǻE<+{᷍,Ү5mZlR9B34SĊc_$񯂞/#L22+vk2*c 0>Y{D_j:TWsRYjntș1A|1s oK@5;@ r?/4 ֶV]oK͎($2`r+1etςG?LGм`k.EL" X>p>Z_gw3+h%C_FN@Y|HXpACl~ E4HB I_~sG5ß JGƟ4i>!f9oBoc6aۖ| oG`luvit?G2I"JE1`wx{oDVim̒*]ychvGL_&m[OͦKxv9ާNk~)x!s '.Mmo-HAbcv@'#<%kx|cwj1.Օ+YmaV#y`9S( @mg^^xdkM}/_%|K{ZctbgiLm4ʍ=v8^MN }+Þ&\/Wm;zzD$($u?|⿆!氚~'/re5j?~EkCC\+P,r,r:AE6qK-XuylYbq6ssWg_x xLM:NC$7 *\D"HoJ8eRMIGѿ^4,I0sހ>j >-4TԟHׯtnbpk+[<LҝėZE_Ö́m(Ʋ[88$׆ϟ.k~Z/|Okw&T<@q-[Ěŧ>խ|mVjJofE|֌ِ$I >!c45/O:dW{f69Rv޽\@PPQEQEQEQEQEQEQEQEQEG>#EQED'o}kk/k3Qex{LTPc X =~yW C^XO%۬V=OҺҩ WfeMl|?a5juo {^~vյ1Kc(@"/JIS")f?h?#@#7Z(mu*g,Ʈ*cS soA;4v}һ:X5mhNo{ #*ΝZiqZXEgkڐQ>M(13HѩϿ>];H!hlm hv9fI$ՠv3U *VI IFSZI~/v c֚] kP8 Q@ j<J^sKh:n56s]Io ?sW()h6/)J:P$de* _N,tHl- [F#rrprM[cSڲzkiiˬj"1pGZ٢n:(RQEQEQEQEQEQEQEQE(HiGZG@ ((|C[hlIgj%~Ehʗ68fkH~$vL`~iarżч yů ku(--$ҡD| ~b6 ([M [i /GGhB"k)OXoZ}`ڒ9%^id۟$W!_hCY+kZxɰJy33Ҥ_hmb{1apQs0s~|t?Y?"s&fMQ-zǞ8-t~]Xb!' Q9!pPm׎9elgfI˴_-dFUٴcC}o>"3})CFP 1־)>^+Ѕ _Ay&A"b?_Cge,d"؁[vog< r Y!sy*BՊlǡ=;T,bQ9:8pkľ Ҽ#^oY^kvKmkmT9@f r=64ÿ.5־ ̞3 C*/Zxh5OW`W89\t&V$' Þ7icuqFr$}{#ӼG?iAh2YVmb!rA_`cbW`w=y]f \H:.I!;-|r,ͼli۴,>jNOIiږeP_x{¯e$heLWϿ)h'O񞁫_-0, vUx 7=U4+KI/l=\s5+/]VךCb'I:%&+<-iGú]-SM/N4GRŽS5}OSХἂLHv3]][Az~5HOA#?~sxTӯ1w_t-bzlO:g[G .N S{x^]MNlONXX#NmGd6@~i3K| m#NklnN6FGlZռsvSt>e* wWIw לW͟fE{ywGsV6uoav3&U[v_NfKvYI]1mRiP:#X񷇼=oMsNӾWI=W$guoh [6tBnn1)='¿4tkoh,MZkey GQ6Zۆ $DجG͒3ᎻΡQc|?uBRy7H)\g8@yxz^y鶶HEF 8ko!I'6 =Akᕭ~VӼ1zL,ПS.&)a+Gm?-~w{FA JKE pqVҞQEQEQEQEQEQEQE#i6(( go&'&r/[Ocs ٠Jmu Z4f_[ 0H9V\I,ֈ@듊R>86Jd|Q< E]J إ=q׶i?ƅkf<1[ۄY> 8bkԼ3aKqeX۶[iO [MwLhrIS+&]*Si•*fu:AخƸvU_S8m%fNu[L6 2*`>i=q% fBK!$Ӡ Rb#Ku*"Q>Ų%idapqHN(eM&^/;ߊ\Sh {tGRKJ3ހ+:x;6 1#,F0ǷhP0ҿe] ONx_4:JCdaw@'Kc!0DL(>cV!o˥GPF֬Q@:odmu{>nӭ$zUf%C 8{t#BLSH!piH473;~jhi&!_=k~w_xRmm." sQ&:(XA#j ̠5?>ZU6{ն6rR9,[ 'azu!47OimV='KK{xUIO$RkDnu+SiI%QEQEQEQEQEQEQE(HiGZG@ ((yRwXK3w&OJ} GλC?8ӊSCIh|E4s2}$\@>nx^x{ˆw4~hy,:7>p15fv6:%:\:#}]Iwqa/,d[yU*ykIVct%ICcaϧlw}7Q*ú>W+h %3#ֽ{^&-t.5(xʸ v=+_}nM{ǁh^%ui{jzFLl F@ʤ7p /_~+? .!E5ϋu{IKm"Y@@]\L(|[+߈&v},j]%W L%@ނ7$Kx.kT4HGZhm |N5_M_wxq\ZC{e7[ C ||[Kk9MG4WZo6\E䃁(ݨ5t?WwwZ)4?E ʰrysH`*0h*%uã-5Ҿ˓Mb A_|S:~+|qt+[uF,wrʥ|n( oJoti>>4V894G%^9[MijevQI `DD!%?tVvFHKaE{$Bi|ٌPtW>1Ԟ}S@R qۃH8vϦ+ C| {-dž)Π֦0B }E|.>4ƾxKow!7b]3wu"M 9ȗ`ze+Mk1SؑҀ%=i( ( ( ( ( ( ( ( ( (u~#EQE:{jVVhnc6tB7Hl$wnoq Mp2+>-~ξqͪYKgv3`1?W'cGRdf-282W I1 iکOat(oy|G,+[o2N Vgjt[y1zq{-ǀ -M=B]qWg_<{'ửOnPdn4.]'2C\FKx&'En#NAnw I1?6N{W[@w?>KѼ;bNW\N/,-#Xp7ë]sG;PcԿ5[ZfQg8ϭ{P9'¿؛MߌmVt^[w"7uR+>'$KGirv6oqk2"dl2r+F ~>𭅅4gn ٧n:YYHv$ڭxy?S_:Oowwq̑'bcu`bt czVPs@'Km ZkCS傅Rí|o@7_"{}ė6wE3$ nR~'nc^$MR+m\Kwcoj+|wcZg__eI=:]$sgIX|kwQ_ x_/|KӥO|+fs7y x'o_? 2u8,ZĆ"I1fBBp{!8!o74J"UpX~b x7ke事ê\>=9ŹaS澛<3*-hϑ\/=c'HWx2' Mbk=^c* 7.K; M}I/]NLR49lg$u^v1]n2e6Re^o]TBm6]nCRYۮG?Ŀv1Gz&ey@{zܳGWm8ݟFji]q ٪GWc_}0h^Ykxw¾guh,PG@%R rX}kt^0MćskzX%K,@$P0NG@b[#Ҽ&XxU_%iھ󋅂M"2JҾrI ?h t[F>@Cȣm|ڿ- o5F|lS;ESmtυ%[}28nݍ,/hbckxx{?Ưsy[k #yo%+;8? <Ӿ|jO n[imIymXq(Y$ Ҁ=4Ѷq]_IMW\~ lguq󳲳k mg=j?lwv5/M?MVY}L1b`v{>F1_*h]-GYFi& 9H.Q>0$ kׇ<v^MѶYc) >^ ڈ[l|KzQEu3>Ϝ T *ωKᏋoÿ ?CsqHe]nGlI{n6͇ i׺woŧG-ʀR?4o'5xPλ <].= G62iz9 qkQ=Nz_|/'xD,g9ȑDF7#Zwg:f\GEM>BZO5TE0Һ}>_ͅ;d_&J쫌?&/3׽>g.xGK KO[.4|UYq2FIE*y?V}NVWjrDcI QD'jG Iɯ~"/PxEGH25%Ϟwlb~p2p:rk(dm>mkGT5CھBُ7٩|ax⮾#6KQۦ]gE}IExGٺOûH ֕rktl?WGgxVo FJb;sCuT#ր>_㧳WQ]& neh{xѣ%ڡdvMn~|)25Hb ]*UT7}E|?ً 4= =_IpAaΖ;e@ci~Ւ]S{-slnfM*]AK+^˧3;f-3zt:kaovF\R9Gom/VT_xXvM fKAWh k(7G/[@|3oVC-ֿi?_#J,tXmi5#~g5ݎv2*Ox5OMcm49[W%u ߡ⾨>=ch7Iw&!y-!3=ʹ@>@9+q~ˇlښh7:lsL'U $R$R3_vQ@ |ixcMxfYxJeX3EcG_ _T.5Y:Էsfon#pPXqyBxEPEPEPEPEPEPEPEP֔u~( (85 3x[mmXK$L*4@NֺI~#bKK[k09KuuibN@XrN0+Z~xz?o|Wd6l't;;⁞Ey;sun$MHy@sH태Sh-[c'<pVuI@ϵ-|m5+~g0e7"(SϽzYYXx6Mڙ"Mr7 r+uOڿV躭׋VY,8XHPp?j[j>n;YSk}20zRוkjv7 _YGOi|3<3JNWx'xC9-֏{kwzG|OmVq=&v~V8LA>W43uݍ7A`<$ ˏmcĺul%.-!IW;)#@?SUӴj~}nk;"]xñb i{n՚& 2|m|%1]ZKGǾOҿb0=k|@5 hShtd[ лwmÚQ3s<h|HO|h{mSB4 ۛB S=k̾ Zşzm4R^$rv_I~9eS>-_a Os\:me.H2W,U5 {'v69׭Uhtn5(Pv;C$){W^mgU7<+N+< {p˒іX#+< 6'>5([G6>ul,7Rp,qޅ_p(ظ=)7'mSW[w|*[n쎧`pZ'|M/ ||S+ˏ=+w|œ;ʃړ`q@_|Hm]ZxY x +oǚh_m<7kkltw̹qvx ?|T|K6;xɍ'S"?E񅷀w4oxPkw-&vgd&0"5U#k'Xo~KyO$,B)ZiiֱYXDo HFob8ϯ|.|M5}'.|W|6V4e?.y^14eI'W,ultȮ0./6NNN+bOjxC8>ѧ_d7)d ߰k^'"P7KX:餬1Y^.E]kU×: jw@-9Fz,G־_xvAYF"$I9$ `5-[@SmRk|ڪ3/<+<=5<'X43}ށ >#wG8a u9u'zּUxv]:=OQMFlgg+Z,'o?ր$!#ֹOtf6^X7[͂FL 7)U(|PhVz+y}kk@=ĤEA98@Jj(&ƒlhvt>*H(.356q^Aa{iGo,J@r}:Ӎ0ʣӊhyzO5{}iۅ'>dѽs֑Tc^}ޓI)8(\oO? xZ2o,0w8ieH#WGD : sM95>(. o Ƶkbmo#/@탅}+9A*OY4*RnⲰfڑƠO@ )q\ޟ [_lẹj1BM1@8,rFxPP$S@pNқ@Q@Q@Q@Q@Q@ :?ZQ֑h([M;V 2o21BHV#.>. -/1\Γg3ov|Hl$!f>R>_ hq(aBXludt |7?K]鷱ܵe 7) 8;+L;}&$gKaWmIcMCXxsOKK}ggaiٞx&:onC)H %c)eǖ=8T?4%rWdvVkT|H[> *+7to~џ4Z48{{jg>J*Ђq k_/H|Qqo~ h\C=e8^LRI-᯺[=ar5FE[Tbd.p:zV==q]Oi`HbCpNNO|`|1C \k_:L f[,b2('b3ڽ+$ j񅇌AȷDh-sDےTx$ K/C4UhXX@Zׁ]g7# hn~3$8u]^E.h!1j @`r}+wM|sAgx+~QQϭ{'4Ӣih_>ʞWݿK=A϶XmK`eY؜ݒ;W9 *'~^[jRñy(] ]=: W6z-[ '@p~^ڬ7ZΥO2}GRq-;\hrPOL tuJ)~╇䴷]MPݴEh״;>c7u-3.S$6H+D# 15{'ig=9[2+@NEsswlcb1`O9>v<;'\~_xK4ǭxwľM֓Etd( !4oizMe("p 85|EǃiB5&O;pűz|7ӼV xoƨ%FSc&>1xYկ2/kO̊e> adsZb|gi^(2}x`1_QY5!d**(\僆'oxT a'Ypq{u|-׌ŲKGV{(eVߌcZs⿊Z׎?&n3ӓ^?u=/U% 2O$qn#GˁQ#Aԁsa4 Ďs׊l|imi[Y^5ռV9e 1 I_A[5=3Km%ERUYcqq^Q|335I .Gھ,fPd'i*/JM>Hv_:Iso a%e`Yq#Ў1]@ů<|ń{񝠒qdw?i_ixSM.!LfG}>n8Aӧ8M]"$ Ē-Xޜ$;[}7\:%MjPkǒ$$|& _ i/B7Hz7}-x'ܐK1€;T7O7Jz[ۭ%9/#JEرJ;.m.d,RE8H8[Tm6ڤf+ke}rxJ#xoFֱ}6xnڅw7 2$7*oS/~TH<.ZKgrPHJ4g`8cIQ NZ]v`mŚG$T/xSKth~c햰 |v@+x|(NaTZ[V8n t(Fpzq]/y~-4jɩ}/oJ&Tk݇eQ'3_Q_;Cfot+>EF>C}G8zVO~/጗/? i~kOHt8M(((((Q֑ҎրEPEP:mMcC{g0ĐNU|;eGivQd&\lG[{Iۖ&F $2NA3}@$mk%ż iXA E+=h7Q]nu(FJ(wQ]nu(FJ(wQ]nu(FJ(wQ]nQEQEQEQEQEQE/JkJZF@ EPEPfotoxx-15.11.1/data/images/texture.jpg0000644000175000017500000002212112616075370016234 0ustar micomicoJFIFtExifMM*JR(1Zgnome-screenshot 4http://ns.adobe.com/xap/1.0/ 145 183 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?;hegEn@obYI¡|YA\VfzlP{g9 sP36MԚ4/8<SKt-piNF߁"Y=O#ߖzV[{~<~cJZ:k(s&iK(zJQd wo󷑗!qK,Qf # +X"} NX,XZ@NZInGvgH*;0$3JpaYFn\u eH?eH?&csw 1![ 4}} K}} Kj"t,[S}qs2ǵ1|MΦ-Vm2)cz.>g>ߥ >g>ߥ u=gTbt)<9#+b]_S4+jvd``dmt[?/Q[?/W$|W\$60@;hQ`g(+쑣@ s+ 3%A~(%A~*>w$&|{pj{_YϤYϤ“}~g>ߥ >g>ߥ Mo vUGO6tGɢH*XhB0/Lo3OOjz~efT&.h%4b(U q>vb O\!d?Ѝ7to[:o"T8(߀Is *OQC=?JSBouiF(WGS=D,A*nmX$}OҏQh3}R4}^[+Wá>G( ե3\4Ę| )_Ck(m.."_02F8{V(!`akQ$yy/q4 kKxe2[1h|ނzC=?J>Eaa\. 3t-RK5͵kMm&+Mk}OҏQf"b8Hc|ܞO!d?Pf7G( o}?z~}Oҋ0УtG)N⊉n\K_o\TՉkMS^VO3g$zStGpA(ߩϜ1@7_"eYC 80FlCI [/FK|Q&C+.cYR:36la6ߑ@L }N?/W\|4ۋ- if>S~>pA) ء6OfxmoSP㵷/ܹnK`J@.!IS;\ddsO#zI ڬRKX\QF ?c3[%t+ h Hq! t_o\Tբ!QV4:u{^\2vb${q|=Oyݽ{bmaf¶1GOҺ(>ՠkIm'NR.?(ºtHͮm#eIꬣEuQ_xt{M25(2)-1ыWBqA ;݊!/5>ɭ oc<TѦuTC#HX!'8\h0je_M @zqF*.?>u=6(Q*[I{u!Ѕk  +9n\v' TV mf8cX/#QiWSծ٭ӆJsY`H ;@Q@0C,qKa=^ESz?ԧfצ: eo32r=:RڥѾ7/`6Y~v.~{t(Q㷔*OF'>vGQHغp&l'=A=3*MJ9WTծuѢCcqZZCcil% *jowa.+=ƙk_j2_@4dn_}u;'ѓi֣/SFO7/eZ)?  )o칲>^ *%Q,Džr_)Qw }`mx[%U5cN'y)*v_M/畿o&?íj6"±<2șCm$ ~nźAo~IX1@`{eѲy[jxCKk𲤦7_ ץV)KmsT8m,"9,g u?٘Z"J*Te=Ajvc-ZT ~PzsS"?7Cil;go0Gr3sKyAOD2ohb^-jN ^G[g#iy<e&~M5ϓB#)g6O$+!w_.wʌ۶= 1v^8?hv90GIU$?Sj}jӒޛqbngKb ؃?ZXIu&y8I8j-ځ?-f6řϰ)QAy7iڹ^7=%ig.Y0c?o`07_=?E1~KKl$$Ri{$6=2;-ouA)V9}\·_E]{ 6-p"Hr&xY&[?EߞaZMf-Z̗;H[{FSV9GW:S[S,rO,`00־ͧkwnc#U$ظ9[?E_9P ͍5(-}ك1;.;QRPQEQEQEQEQEQEQEQEQEQEQEfotoxx-15.11.1/data/images/favorites.jpg0000644000175000017500000004521312616075370016545 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|tonemap| 4http://ns.adobe.com/xap/1.0/ 201 593 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?+˩so v{ \K9r=ɨe >\C(=~c*zT:oEbYf8ǫr8V!h?{R8GqqZJ>8<@{S«[ cߕ\nU6o(~n? $Zd:*%mq$b1s@[Gq}/V|ww7:)-FI ,swmJa#8?A%Ҁ/m?W[U-/g{sjHwd1Ig}5FD6rs=q}/Q\Kf\Kym?W4ym?W[Fh'q}/Q\K4q-ZV&mv2֥yf&Hz_a]|4h3w!ң}3NT'iV.g1Z"@۵q^ >o =,wU7{$v?6?3NS֬vk]cy*MN<ʭ7Jn,&{LӔLҧr&TJ:cv֫uz|"ѱ-?"Ok22%#ŴSa::M8YE.o[Oqb[֮EΧ{{$&Sh'%B(mGWt{눢{{b(uض?m?WjZ̷Z<6s$.e;X$zK ZM1\L($@ضŴ\F/,XUdҗE֫Qdܘ7ybh[OqcZ[֬ Śkh'bx`~m+wh%(5%B P#gzض>)ҵ,iL&f2,qI׊zIOlp̬$xEcz\ض=[UUh> @PrF'M:ݵИ[>D˿=j d4YѝF0hXxǯwdzHG +03;LRdb 6镦QUzqV d4X.f=Oh(p=8Hlb;n3뎙OA}>h 2;;h8ʲ rs>EgmVE*c>H?G atSB՝`Ӿh\3W>h$?Ѣrh\$?ѣpFy5spFA},3/mt_H5X3oVu8"rHϭWGʝ7X`1sዯ9C QxwJ9VX 3@p[d4}ӳ :aM *Y!lIS,+9!&CyZd4}Ұ\}Ly-$2=km̧h9ӤkK+ңy+I v(K}¶>h$??Ѣ.c:bhz46 yf=Mh?!d4B22$??ѣpF\̣̩h$??Ѣ.AQTd4}f (*C}>h yy?!d4Y<<ʟpFC},eeOHG!ar22$??ѣpF0GSXE\5#]cNjUg^U!3z^⋻-$WYM`èSk,̂yiBĝ7rNq.ok7`H?ʧR;8,pYONz.qox9 2Į{槺ݮ._ʉqa'"(Q@Q@o m֭?!YRMmx%ul IEy$$qn2 ǤyݜmXZߊu5K{SptM;r(:Z* o%*w sKxPxIV(#1&D {tɣъzdLo(pBvKž%70y"DVX ͓=)-F;Rs\mOtmkWhqMMFm&XԚHuxT BLڙzAndb]NI՟k;W%| 2EWK5-+mI5Ӳ7? iMfe[p]*zs2kEˈpD|@Goӵieu!k?|$ 3DZcj)K_T?{RPI5^\K6 #'Ԍ1'M|9ܡ^TGT?{R_ʃ]PI4eC?'Q9Ps˹C*?Ə1G*yw(eC?'^TWF(A.? j(9ܡ^TGT?{R_*yE\O]mQY᷒WQߞ2{pkŚ$04Rr)u&_:xN~}=j ] f~*BĒ8=PgMnD¡r+Ӟ^ pm)1YvOqsUMwL6v%N&ӆ|qOp4+No5[9u[T"푊8b==Y:$7 %&U!wQ U uHk_7jO{f.g8նВzk>Kmr7rPw<.L3Lu".8VRC)+W"/4%}kI4Ņr8@|HJ}onM0|/5tГO%^9/(FvvmO_`ӎv2=߭X *$%1R[/isO ȶDҗ s؀AK]N=<=vX3YyTPJ6q.<7\s<;upڬ qi; U70 Z*kvąmgrI8fE)2:"-=aM>61mjXڼ¬QE (o M֭?!U`Ϳk`ttNPqӊ1F(899* Q1@ (’ QӊUWme 'gMd=\`zQP9 sAmޛu+" ʾsɫlSs*oO5crHFlzKv_*Or9w >oSgM?mKS/Dsz—rUmKSOI7W;H ch.'}O~*M?6%.>'}O~*M`Ӭsre ۤn.n߇ >'_&zdU6%.>'}O~*M?mKQ]v,}O~(t?ܟ?UoyTd[ PXtܟ?Q?6%ɶ/Fuر??rUmKQm=o?2_*BcrGۡ«d[ &zdUb۠CWɶ/GM5 ŏAn߇ m=o?2_*oyTj]n߇ >'_&zdU6%.>'}O~*M?mKQ]v,}O~(ld|NaWɶ/GTC/~HPs!a$zZ)r<<.m1!?ʊM뿪*U뿪*@.h&HeYcԲ08O뛫mi%K팪* >.K±2]ƒ{[﷖8n/3 w  q@xrHestjs^$QO ]{'Cn\tyv'xzXS&oG}(|J1 gIq;'[:)9ZSU]@ 7Zbޥۇc}-%fa.6ypF'ED<3oi{ecG \H6lK+ 8iZkzqX6IZ' 'VPp+F#|)}[E}󻫬 K b 'IM{i3fi={(޳/tMF UhBK#NXiZm,f0 l=>fPev [ڔؒ#08[)%^*Jw;)%X7 Tz&|o>{mF8-6WJE,,i~% $F6ѴOqi؞/Ȗ. Մjf-bOjk JOmZxcC/ Rv8 n|O}g=i剮vp99Px9)#goPNL{xLn}He+ ^eL/yG<֌$sO8J]+H̋k njϩ\GՀyBpC| :dsֶiX(C ()mB;:V+VhFH9LJOUX Ϳ jz_0glLI0t5>kqx{e$wQHY8 $F+}h}i5Ny5)mfkFvpzgҙeE 6[uYF`'q4n>3ExÀ\IeU9Pi?b|?h|?hlQ;; F*<O&<O&&~i?>i?b|?i>i?bЇy4Gw? 'Pw? 'QI6(CI}sMP*<O~7 ݇9+ }4zF0Rz$:(?W?!lm,"Jl|A.i&-CJ dIW m0gO6P&weIP < o-6Y`rblNr'zڢ![;q4w)Ԟjj((#C@ \XpQʞWA:Al.d4w]8a='V]?Q5_ ŭ4PKr3^My1?HظQW"b;Ee k~)TvԽ +g<k4"[ UحF=78]:k$4ZdG;P ph~g<j6mH "N@#\= %ղ27  F+tb_EO^ ?ʬUx?ʬRQEQEQEQEW kKc }-hNh̒HJز+Wt_jj$Ģ TITB5:r;gsNJYڣ.iO!vF㵽q{mIJnHw_`BkZB.Hagְ[gQ[gYp Jdҕ8&%o+O@nyb ( ()mSBV [6UkWjj }֍֫^Z3_d@w637SFF &(rh}jld.s~$lIJFw#}An>dbh+w{Ux w+F39/\ߺocm|F&&WP$$%ْ>vO#}qLF\ߺ>? Hmlex$T JJug}?lq~PӭY_M+Pt}ϵؚWأJug}?lq~z.cTRh0g%\1'2ql`kGG\ߺ4]4qG(]2ǩr*kGW}?&h >\ߺB@RǠ5Q&.#Y`F+JA#kGW}?Vn 6G Uhjϵ>jB̀T4}kG4s@W}?mݵu7wPXus' Cs~*05t9V(;X_,$xg>P5ootcsy0 wO$t4Ty[ף}o?^ETy[ף@ϭլњ>Nd0H@1N;~c4P5Ί5LFwOV*uUR>QK6C@ E.h} %Oi4\x /;9\8P˷vݧуhͬGO-%Φ>Tb E1 ߏ+0yWGtY7`u#9]/Fށ}ai^]C= ';OSV?\a~u_HX+{OO6c#e|NNGj-KyCRpA%Vv[D"% :@^iK>TO'˼TyRv/?(6C@i4m>E.h} Rڧ*?mެ:TЅSCVͿGCSC<7n۷aqsZv~|mw٣q*d+u2 d3$^+oxn:;}[̚L硵xٸ%h_wʪZUhE Hdoߘt(Oz}h}ixdn 5I\!;H'cr>40!s]̗+n+U4Yϲ=RY4c w7zjaymU*fRH>'QT<jns h];ó`H'ֻ7Um|n?wiD9HJuw##/n9T>7USԿ;?+S>7Um|n?!=tqU0ׯd;\ Hȶ_ϧ֥܊}}O1M;\Je \$j{7wFYdƣTb dcI$$) l0R~%zFF#.3{fG'鶹bqF?}$a$g~hTXE T'mfu%.e6M25[Dw ~bx<-%R,<W³> 4}?iLlO0Kx;IF  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-15.11.1/data/images/denoise2.jpg0000644000175000017500000013114512616075370016253 0ustar micomicoJFIFExifMM*JR(iZ0230c0100Fotoxx:mashup|trim_rotate| Fotoxx:write_text|write_text| Fotoxx:paste|trim_rotate| Fotoxx:tonemap| http://ns.adobe.com/xap/1.0/ 8 558 664 2 2 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?薚]!AIh¦v/5bT+k+2p&Shh*׈Si^::+\c`/c=S|)Nnݼ>JAAV5ߗ|owv={4r,sFWn䴵渞0DGw zBU+3Je Ϸλ ʎz\jΟ6q[]h6&X#n4sK>xCLM\YFyIVC× b)>ue6VBN~$6m ZD΁|.٩k%}nYWvG4%З;o!G*ey/~5;$bC-3vg5xVOAM<io `;FBI=5zZueӜciwT|$?53-#*+ ӽKgy@pX.=8<]y7F/ ?Z׶-iGk3.Ps9I@p< YK,/RFݸۀ?z9pO2k+G'/1\vm 1k, / oݻo/Pm #¶EOοֵ1\Ⱦ|*K,wSR: v0{~B=7Fy?_a=mn4}Ji5i27B t~ a(uk+YJOr E]i>!җ]ZG23ٺn>ڹxJ֟5ɴH ]B'mvhMw}ǭiTiX Fy<,n_Z_[QThj:AZH"h-RHR:\!'< @y`O hھ)&lKg#ٌ⺻oa_9&e<$];kH/;17 2~tkK}RLdW~:'wD<(F[ qڳprv8qFyK6os; =Nd˨1bsϨ5}j7ltkvǵz]psU=zNwG&\HNZx~]7J5fi{]I>cp;dD^=*-B8d8Y[)GtCj \gz?+e 4X*YZK-WE$љ)co|1WLks7ih.miBF=uZ:̀Lܧ &A?h[ԼIxn]S3dKc I{bGׇDmC-L|0ck$ж1uKhV6~$MdBbeQW1'Sm65rаn[^dbC&۠y+HP:2FI; 9/ϊdϠ\[T@}+C}0CY]رIg,I1}Bu9^II.@CzԫnWRcNΘE^'Nm+PH{X^e=3W< u {ĺu qR}3dv'ȵm:wm+)'k;ZHeD+\,r,3SitQuLܧ?rS.nua&{xZ_*h]2AY嬖FXQva؞N+{,:>SiF?@<[+2Z%FFEr X\k5m4Kq O4NyI݀yz3 c=?֩V^X ^?,gJ, x;&&6-KbʬIݻH'VΉ^>,d@6&1 uwtҷ4['EЭ4l HfE$n8:ftiz`8/d'vu l9;]Tų17ڑӖ̬o'~ZScp\qPi;wbٿh#;\i"dGE܄rCQvΎp\[!Z Zu 8(-OsYl:ɚzuđyJ 3(ˑǷJhR?,BBeIv lqysX:mrw1I >h坰[6gNs5OY6:>^ ԛV,Gw[{ǔF29^O\6G4[J ,mGHE%e/bwC,E[''֑c KbMNzt'f2hp5un:6[rPNp=j;6VVCؑۃӁzַ4ċW06,4ۥ,y#`GbyX@v$c]_ď%Tݷ"V|k sct5Gk)Ӆ[V&l݌թ]]%mIL~a#ҡ 2̡:vŰK2B孎bBq(<(-tRMlh cӨ~=yHWs/#s82p6U p;p8*?I_P`gb([r!'AXIRXf %(lp~*u@Y+tb h:ɤXx̰V "䍇ۨjKd0\lEF/fg޺-7KbH#aǘAU{{k{浂xdhMȌ?+ϽΏmm,al ѻsIrq=Zнs4*?Hɏ'+mVd32 "݈O<Gisi3<$*n%dPᔞۉ8zqVy,cK-yPLUAʈГOČǁߊ۫$Rn#Hm֙ÞSЅ nj烖SʎX$zԸ!Y0K恧9K3*`""v0^-G4l(Wp ~C=OZXy֭hypK!Y uۖ{m;[8==oղ7 p=vЫv39H>ΜMDϷZݱF_*qR]2w1Jl( "OC# wyzƁo(wg҂Fr P-izYIuB[Ɗ~XWۄm(λBI\y0烞3WJ!-HQ2ۻ!/z5 ,fh$K >ά yI^Ğkk׉xc,䈥–`Whl3"E>Z6¼; b<3䯮+w췑ʶ\fpF0z3ַzř%xZh/1aQ[у85㧜63+V[BX Vυ dFѷǸyx9 ;g('lh1#s#c =/.^OeU,qm<ᑞ~7(e^X>ϸj8^pvUxVxڰ91p5|1Qɖ9$8#=ZҌ}RV飷{n 6e,o3AV+$l.a,pYƛv&!Utό8ӠI"ۡWޜ଩櫃۫@r *_++ƍF% X($Ɇu u8^zRl$hExL H}?Yp%jC"IS8iRSTkx݋3 ]QC|4UXZ >r#n:źEx[=p&pˑGUr )2/̠ioA7`1dD "_ )a]߆Hf6^Zo#\#MeW-%.PHA9Icc 6!^9ྀ"PpA*6"i'\28  _lKgP?vq+7(2ݸgx>M㗑n;o:y#I||vY/k'rڮݙ8{tɵib)>\!en#-=4U]HN)F}hkIQ`?8s=utekGhF"%8H;~l.Ieq ɱEn?כhy ZO,UkVկtK{kWCcoCsq p1B̓:n\`t<޺rF`J@19-s=qӵmzrC-w!fN%k$eCvHPOcXi݉ԼPbLqy9@F Yvm;)>ui=zyǪApW~@>TjM,M)$VqF͕!JsGI:MG`EY̊p<@X"[oG-ʇE`0 E5- rRmE"~bL&d98=j;BdHyrѲ@s1'̈'b- u.Po/99ddآNV:Bd[LpNH뎔Z0Қȩ$ez`}=],E,H2Ej GlҖэbSi#]۔^{{iq |+yWȃ/GwF:ܛڎ[C,rJqm1䜌w(X@dI!y8`s׵S$ |)XLx#;ƣU}o7X ;(ۆWe##T|CmGAye->t `aO'8闦f/$bX9߀nzm  v1Q5/6sOH#BHeY6EpDZY<0~[Kv]%wRƹջH&k#%6VRq3ڭ v#p:¿dtA$ط%@KI۴ԩ:0m7 9? uBU{dKXc`n@=}j;y"h0ecnXvQ.Ua׈Xɥfy1ˑʌNDmⰵ+.̳]\G\(;_#ރ돭YXZTкI#3iYgHಘIrCdy撒֒ .lnѯチī|Դ"x$Lh$d*m*Kl-E9%-qM8:4uLHMlT ivEmyb#M,3!Y,|0A%rS]~Cs} 0vD ݐŇe8ͷ\b牮mdhx$@;w9{Аoӻ1U)8sǘ+(G9J^2xv9yX=8pV/c )V\:S̲ҼFc#L$OOͯk0lcs[+uaQ=,GA9=EC4Qʆ3(M#<㡭o$ +BMW@B,AL }A=O,vrMI. {I3<19\5%foeC%ƅ&#Z%nfcێ8l_IR6il:dez`dwm[Kɿl下:=btY7{yO 'p:㡏MiAX[hL0nNsZ}mE. Ln+u?줋d~l!Q?;y;٣3^=3MRM}D󬁿I 㭤KYc#f;ۛv:WmYm5 O%|,|b@uڤV¬hifɁb\1yZ+Yz5W-,#F7:ffH6$}`YysW1hy2$D!;|Dd QAb#)O9081Ie#]˔{ORDzt: ")\I"K(,[*-7[0ħݔ`|yw+!1?sye1Y L9ɨ/ùjr K{ˆb ʅ g'?$wb.5{#%HeC?Uf8}udcfJ 3{?<9_$`k:+GC'Pط ;!v$y9 ,N*68ǽ%LwdC.OT_ޕ {JG=;>?x6p2Fg&Iqq\biaӞT3!DE*w 3/ⶃFV@hGC)t֘qq+?d-n1[2y5gTS%R_ ̱i"N5=Α 1w-J oVor{L-KʱKmB?aRi7;FȒA.!e ^8z֒Z'+#[-'rVX׫@S9+S\nn؎mQ$)STs2LLv mšFAssqO FQI_-rZoPeA$sǭzG6iCIm.Gy`낅g6S G"Yݟn~Շٲ f[ k8^֛kҰEy o,*dcSwRJ ຑ<|Kr^;si{dHH!J 'y5K9ZfV!OO^O_F^TlmRNsqgx)%Xaw$a%צF{JW.^}rXGD8 w5@ "Y.A'"[pxOťkRG$k?dfo!8pˎ98)Fmq\MI.Tm˺%8냞kHKQ:ryjO8h.^t^k܁xZBՆ^%c+J>!kJq gޛhiv NROތ}5R.ؑd@@gtt[h>&eYhE'QpO^wq_敹 5ʫ"12#mdxp5 Z.):\fH6õNA<[!J׏-c\9 nYKS4"ȘyN˖H (@_:g=sB4;* VPH4T+mY~I_.lV9Yn2E"J8A:rkuıtO[+?ʢ4Uq}TӇb-H]VeY6qlʈ"_,< ?'Oڥ.0./NO$y$[$=MT0G5+yVmb>\y< Г9NPî&RHAV9a=z՟^]XޕܘB0wۂ8=1[v~! {hb{DW*< #r < Wt#%y&IP\1$9lcSY('$iX%iguWʸ9V/ŨXŨ.o-XeKc%A$myM %Ȋܬ6FP*t}Ej]CJ*y6R:u%&mQrwCog,±Y Pg?q~<ߴA d߃>w$ C TAi+eb3 {ָor$Lm9C>vsb$:)Ojoy@D1!GL~}M!8b?ح5K?ݟqߩ:`%m5Ϛ|<jسuU=M-ƙ@ Gs@a"F 9>nsYQ[XAs}gr>d<:Fʹe\_ߏΗe:dnI݄8q$m@BzkʿdC<F6C[=jh|Gm<syVY!2"U*vG3<@w"܋9$Ku.'[4|tk Q\ǿ@n"7VK[E lt[p3;`c8gTn+d1"u7s>X䌀qުYjw]dkfJbe#$î=V m,[GQH 9uo ! 9ݏ_Zh_3`_+9XrYbx׎}v6<@RQarػ؆<( UK%>ss'$11`RY@+ִ?$QR(>V8M6Ē_h-CרNW̡B)20)p y<=RyFz:]ӒX[`2LkpL+|ې㑜X'ӌZM Fȩ9 >5I\y"Ms(31$@[FW庍-V4 ̍ (n8 {ݱQaHf֪( *xA*lZ?nВ})Q;`u<"ƾ$!6ßj6Qc$SFmAcB(O GI)y/{%ȴX"8bdV'1_hr h@̌IP73Q;iws!"Y K/tv/_ BCTZ7]|B^&@$'7wMѡK#*e]Zdcg|gۧOVa'" epc t:rGzQ͌jKIp4g匩#,AL<צfeD$dƌJłyz$#s]Ön84CЧcjh0!%їNKfM ~lLڰ6*5ͪ*+ ц3])ln'9^*V(btiv7 SI;4Ӽ5w7㼸ā ,AJ%vlk#GڬI r&1e 2'4:DCW$!f\px> *'mO7<+q} I`|#K+]YXmVtc@IK#5 K|Y p Gjy$B%6|V=k1qA,dqRB}ˏ~*۶ZNʳ !^ 8$RA;Yq0 ZԻfoA2G,y3:fT=֞ n_*K+Ю5d;3cx |t,rQDl\3tAh=b=:W?8ۄIdI#o-Fw1ApH3sKCuja|'ʊ4yhոګ̑T[] йcic"Vv?t9`Ɉ8X# Y,$q%X ##?1Ul $' 8Oh 5~_߼0K_ٜ2z`^T# D8Rl$ oG~g~$=ǧr}3^W-)|d!z4ߺea`[,<\x)?9lp`H2,N2Iwd)IH ]3iq~`9ɧTGP;a].K F݂x<j! q\K ~sqӥ#\ڿ3wwhUCoB̮w+Oj]I1F ПΚb$n\4+sQdˉcs3 #֙q#͏v]F@?*fpʒd4VɁy} M1sgI]Z^w鎟Jju#>ATuoSިI, n<`ހ6H CcU:ı/abDoI߭1ﭝٝ19%$a68gqǥ8ڐNK l8uIn#s- auk̡ABtFO]z|Qm!ᴵu[[#!G#T^&ƉݠLM]>T9O 8YfK}|)ߟ>g=1Rxh`frj|]u;k(N5f [P6~b\Mq ;k1oL @$##a=RОe}r&hg!6\+y\?\ZcTyFh0Yc_~@m;B $ys(CZ>G$AM/5ڲ+EٞB~|X:{ %Юxv-/46T\BҲ!۠fq$,A/qbj#7nbq>ҫerJ'U-jI)V ?3 Npa*7,p>m2 W6ڎ1P_.O ce'q$3;>{{ k;Jt:"ps\Fq};Q]=nFe\FwdO:t_ڌQJAm ZmrjZ+/q38L,)W}kxml䕯m_VT yoŰF4Xi"H+jM*HrwȽ ޘ5FoIheiل%a\9XeHعqqCcuϭy=ݎK-$D204(%I`t$)o1o!0)M-X94{cGI Lvŵ]ytǜ[Ǿ(p 4LH#9E1X0/3f+hKD3v9³exky8ܓ\[pP1I+^uEXq.=kb,V8噾\W?ʹMR jmxL.I%l9? DdJXUIV0<tފ*˴+5+MdiGY7/ 9,!E ,P,9SnJlb\aLbU,ͼ'?/kX%P|+(z$y'qRng<:fB[`@);X?@R7.@NkMm-vyEcu})BOՐ0e9ّ۶j1 ? 1u!,l }:֜fS*2ERXDI9@-v*#;9r3,ȧ e ׊iݰA\8ɭC`3KqdZF8U{Y"I#;_%NHjH\Ŵ"2 y}֨ݞFlf $zt֗طGXlTTNLHf,P(`׃>d$c9'_0Ɍ]h5c;PN̄`Qj˄Wwˌ=DVVuoo1j3)vG珥Jc`q*v_zrGɞ:Ѡ_ +mʳN )uó#n>.ҦMθB5@Y%}λ[jnu H%HżS6v-Nv\bS 28Tز)mqG thA>4Q8w}J<' |,$`N0F_9ċڎug1R噡bp7nyx-Bbh# bmC,E̙c^f Sʫ7RЧԶwNnL2}̑~)V-F}BԘdP1@ m&?L㞧8̈eBAၕ&2> Sz ROCUJ-],qM|}!U.USG 9$:T),%7#cL`ui *:d2۰x҅&{(RPV1089=Zh|r2x{nn4 4dX@ȑO`2-"()˽D @})֤'8`UpdzzM]-JAwSBy@L[vz>ꭏ=w:reXn tH ,q:EsrB9@ teY`EXdk\ 9jRK3ە.H>n˓8bA15ĖDZJ 7_r+4"fVNxCwoXƲuB䃂󬻏{9<}ʍ0x#xFykD\/cyPߥ)G b^Xdۭ)A|f烧}241,Gc wA+qkm5F8"KSvQI5 *C,K܏LrFGq6 CTqs ,+|^N84i7s}?YZ"N^lfڸɐ  N:kӥ3ҝ;cZo,YeCIsX.zuJ!Q4\8 }9~/Q20/d^D(+ UhhmʪpaN@Sk!fud80H^+J7 h 3x&Rin@B Ӎj"˽$},2H)kzg Rb%;Io8B%;m:H$HvjۆU {đ^\ic'{@Y$b&d]f~B YxOPv?@$au/uE+029? ]I<(5XVH%JfeHrTL?*Tcw?O箺uݓs-|*mU.hKu\;Zule籦D|#>X-pr=+n2Z'Px>Q֡Lv$pNVM\ 5".n`h ƈr0n >̶!Hc!R>f9#m©~܅wɵqI7;NK!A_p9M&Ot=m('|M͑ A'3[1v ?)X:-6ļBeUr[cQٺol&2=zjӻF|eym=yyn 2,U׌o3ߌs} Bʅp|I?7;jy| rT$ǜ7Twk qŧSu(\+Fjsq!mBI/Z6gʱny~M=R+o9OΓ?1S>?\$s eFzO_¤Hdl<+p|׹'gJ~c{{^'f$ngHdxlD/=?Lu\jDK7xPČNO]{?$sIKVF\oyq~bW[P5pw&z]wKxMgUSk6$`TmndqZw!4Auu 4 WX(3㨨&m lIRR`y8<ֽ7GS|c)ҲugfҺCG_.6$Vnl oq'icbXQ =9pu:m?:\“%)dt pC(j/JvlGl3E4m!a!'1I瑞#KVI;T4;%mgx]3+"*wLлQ;gҡqZ2Z8=fDҢvţD\oOzYymۛRN6;ŢK'$&REnHEё56PlOڒI\il" ۊKݦgju-@Ld3H^ ueypN6֭펵}hL=DA~2 YA QI7uUu JVI#W)!\6׵Y}mekee10ʭ3 pc=Gz5rm3:-=#xϯ\\߂`krw+6oD-Պ@HX/#%zs$0*#𾥥_jumxfR^{cLe Q׼Ownb&Ynmž-7S:h}.3t5V5#*];LxK@V[i6Lśk->43/~ ]=gmrPBźwik{E,{̱¡`LjѧҍKhkR,FHYݬ4r2V@P7'c,u[{5 6kkyQʑ)G8 OL֯F_EzRAY&'w~ǠLk֗iϡK8,cql |0`OhOJ{EJ pӿRD6̺q,A O<:k.Z)9$y@|k.> QMFria(7UxUup0O˿m ; wgmߎvc9n,|qK][?,$1NST֞=o}GB#݈]lݏvzvihZ$׷}ݼX-*k Hp*xgzse,^]FZ_H 8f;N ʶ|,#26` Vmuۭ{MԵhki,\:zrv9֟b:Dw(OfŃ9$]^@e<|EqC5@h8Ǽ}J/ϱtҪۅ3Mw6^Ķ|V.If%GW_wZnȥJL_YC}zW?,VEJ.իo#Da:=Ko[oNoti!>$8w|smjxaSeWJ} ?Jy*{& vJvfB5qQd4zY_G {#op/zbY|r} +.B!mޅW0ԥMB٭Bs4oZ`NstU]oFkFӋ]یs*܍}1dщZp ƬNݵ3۹j+&=WNū\I#oi<Ѹ.ݫwo7Ѝ*It,X0 _+kf!_jeSVQ:vd.S"񕤕_ǩu%UZ>aDWIƻ|ϙWqqȑHw*­!öߖF >&irf6^rISf(x֚wfQ~k77s_ܾ{Շ v.}ەYwS([y>j㱚۱`mV'#֒7#st~ bo'w}&@] +$OtI<3gjv֞[!AZoCaZH+yia4I&pJ#ckoV7[4r,՝NMH:WF542t40I++.z-6V*1BtWLf -nQ%s.R?ȭU:sJUBTFMknW{^£D*]vQYbK;6 wV\iSF[# tS^ P~Vf_Ӽ>$sLq8Nrf~Y+q7eWlN(o˻Z-ݑp"n7uo]o]ksf/fz,qYʯTz?Y˱mGL7@m,74BWzr5[=]cV7;._nȏk\Szbc }QVͺ=O-W։W/ٔՔk ;.[yVӕmL(mr7TzقճZ5݈?+RBk]LnRvxAzc6֩#[zrAc"ᶮ*8NaR\"Y~jD+E-HKSY?.${KXg۵[ ~ZլQ~$nem̥sUiwm_篌zčR9u|AmoY^egGF~Mޡ6z3NC}5c53A^]vFV_ _>奈8L0h;XK:y~W|CHuR$c M[ ȬꌻkT,mٯMsk(ռ 9*>3Ӕ\j'ɣ܄w!Z[5(Eśy<7W MfKu26`[hj!$:-\ʧp?^z{_}dksʿnRHn|\_u-P[V2RS^;$*Fv 2&aGi0)[ޭy.Fdi PVl5#(XnW!ީ ur+u"=qq,wrniWGnhή犛+} Evi<5V%aک={P]$7Rinʿ/ |71y!幭H|Yak [ɫ^ġ `lI﵉nVWY5piŹ}W^Mi2[|S w_ݮds{cXǦځ|YM-˹*鋱l#ȭݤ0W{^zoxBИX0?w$9-&Tkdx6S"[y.,(U$h~Uim*>ʵAcok /<< 3 J{E#T{VGYZ0bikbl@{qF HK˒k?5HVp fگ&Sm8aW&TdH~Z[ې3 K{_#\\C7SYO6_)嫏@c75`Ԣ֬.]srJ ѾQڬ,H,fZ.0Yy۵퉷jϚ#Oi}ZIu/5_>+ԱzZe&ip6OM^{xv0^TUTJ5Am0[ѫu[oW^o{ >_&XUgնP]Lۖ# fya]IoAqYym4(*BܳQ"K'H/ղu%H'ɑ/ghHE`|Lj]{wQ2!5Y[jmV=UG@&T<|A׸BeMfKH[H$q3+Z v/fһg$UY{rT7GUy?>Mhu4ܞcE_fRb |71bмcllR|wn-MtA&RQY TfYj-#)GUeer)twvF;E]RMoMAt]ܪe⮢-*/6#V)_nj<[bkMLl6 \}D?w8KUR7+פ;'"۾_^_u2NYj3Eۜn%ȰE!ܻi,csoV؞ctF &vQݨ'9tZ8'L ,krg4p.Ox)3VJ6 Si &dj!+][($mVeӍEe(1%ݟYz޽ 0?yaݦvLI 2M2G'<j(K5֏myNצTMo54l,|+n_8P?+||B6A؀r#{rmeV--t&x>]E/us]Uv7;T5AA(mum 'Veڹ,+%m-b_%7+/'qbĶwJZm]hhSڣ71Y(Bv|>mjuҞfInjSڜY)\ɼ*vܫnS]+Q[XfO@m7lQ^x$+6л4vgE_ujh h߻^t#Kg::Yii>eDȁ>omRh_QKv7ͻ5v5|ʪ/Fh\3%q;~9ǫ,;](kBdn:' n`O_]?'%G<{e+پVw,/g2 |⨏Ś|)HZo] T$nRZ_. hHU7C)y,5eҡ5WV$$@VGy}s/.eH˴}|۫k'QUWb 2 e5TW!޼MBc|XmeީQk2j#h#ҟOἶ,IWAYTH<Aܬt${jK&UߙSrN.]ʿ.jiGp %qV| t:4VUtmƷ)Gqe ]~q`O8[y:^ͽvcf6y^.[lY L{>Ie mh<4.,ţݴ[wJs"y/WME-|VFz_C7a1z]Au+lU5+\\LVSͷunX&11'VQjxvU-+U[}6-DvkF[&]Zl*ɋ24+ mfVgGP#!7W?=G*{EW@+E3Zur]O1x]XrjV:TEnK|մ`VOݻt/[uU+%RAGg97,ehmhqy~[9o_᪾@ ˖mŷnY0)eܻ$_,{Z?Þ ^om_#x?'/WSw4q(TJMKB֮4{[wib%df*x-3ѷE%Hd~ +nO-#Av [eIkV찟ZT?i2S-3|۾m_?UZ1k ?۾'R6`H҇Ķһ#mvSkB讓)֬)4o7jqBoZeBjnvjw7 V۹voE{{B|HeS麸 3̭Ԃ/-Sf #Kk#ܤ6zZ&/.ݻMeKco$wYn2G5>ϔ~HQI,./h2*.}֦""Rͺxf:,B4K*h~s+NIfYa7SћZڋd y^]pkJ;~U^+œ* Tv)]n7hYFRoj ʳDb9fm$P 뷩2Ƃ8O͹+T;M+#ҬY"}V;4mmp#@ m#=/& ?^yR<$U ֢IEnv K%o R yHlet鴭 ;Kë3aަJRcF&&c|کV\k:iyY>]ۿ?z]aQJCqW5[{&] 7^Y>us&?HJYnG kOVtcZhmmۣ۫|6?ڥdk(2-*]m۷c*ccZil77 WzAv+ʭu W|A7]Xn(x @^gkMWa}SۿwXһ|~՚(u|nP|H S=gE'YUC{>vߚ7uhTcYi IQus!&eh8UFﻹխ:X.Rjh |&FnHmY,QQ$kX iv o*~1fMfBV4d+ԥxfZo6e3ެvX3lUHvh+miu F159]!GtVYzf>ZP3O-*hbƍHǦܫ 7g]A-m& BqT` e: Vreq+<\4&PܻpAx֓x`rnkϡxmh9d-*ߊm -"c.;/qrF͹c{5I,,"U 4X`nuo~$nO\C\[Y}M/wZ'RҤH[e>`n=khM֟sSn+cHlUԼ@"S< CMe7x/ғ@] >vx­OP{TYE\4ɾ[jZWV~>rSɹK6WMh[[P~jnWqaϭDЌ쪓=ymTyM)I"ߖ: \Z0j #o̅fK-iБP^bnU~޺OeV?:RG3m4]]X[JGI$tlSw]Mo&"QH#ʺ!b|6Wkj4+Hq]CJo)x=v |bm\ cբ?os"Ro#^$T9++UvG6.&IX",l[4ۼLDsu6I.q ZtVvD<^cU?mS//b$.j{3* O#g'o  TYqwnoɢGPOZm4S&al2y%;)X7mOgIER}iKTHLQXFnSfe=v+[؝~I3ڛvG(-nxʶ*{ԧ9 ٮ(3ex&ƹ T*QEmSDmM (P݅3El١a >Hsm 􈈧h݄JTEs0C$}j Eklc*gZ[K,$yP@ݠn@R'ckSfHh?09 W.5^ʞ?fotoxx-15.11.1/data/images/randomtags.jpg0000664000175000017500000003346712616075370016714 0ustar micomicoJFIF0ExifMM*bj(1ri%gnome-screenshot0230ґ01002015:06:20 08:47:12Fotoxx:resize|tonemap|NE http://ns.adobe.com/xap/1.0/ 180 400 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((g" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?CN8`Fc>>i fR}דz|ΟxG}_]me;B|JYaaz8<y'=Met3HHuأ< 7PeH;"[K_=qU1oF4aü=GM4RubWnUCmTÒAghn[Ǧ6I4M((8:Z,X+FQ 2v运sKԔ_D2C"zpsx_RWXjUd@H]#s+CMe[`2 9;EhRѴn%py)KFѸi+^3kT 3N5-V34LJT9<-|'e3h)7nBw?[Wo :6 dOuxwuHչUS6+6qk&{׳W[wB2pc҄ՏO=4hȮѩs'ڼ>6. )%{ JiaFY#1ԓVYu]\{5}ND0pzzQph!Iz/+47mV'Io8S>teIztokO\[C'f#չ"b?WxWTi#[9R Sڸm& ,5Ks-oIy'C=cwEǢB&6oo-ŬPZcmRIj(B+fQHElp(zv运x_W)j}cFi.mIJ8a܃[g wEǢByy\,]=o {{pwx_Qz/*GE!F运^gg wEǢByy\,]=o {{pwx_Qz/*GE!F运^gg wEǢByy\,]=o {{p[jve%UYdd䭂ח=H&Rkb pA"MC--۫۸~ ڥVMJݎoDӭ{cYva}j<?揳r{{֯Q"<E.P{{֯Q"<Es+_<EgyG|9BWGZgyG|>r̯;ޏ;޵~}( ^wwj(<?Q2z20>kj i PPh $gj<1g=u({%#B iR̿o`R^ Cd})4K٠:v-sKyrXtVn)5}n}jk-&վRuv9Ƴw$xKm2MIoKGVX߸([[=Y {M7%RPQK1@ E.(%RPQK1@ E.(%RPQK1@ E.(( +aqCu%|\:u8~:i-gI e8OoZ@{F 𵯈en鮴I9!Tdaei'Z6zŪϥ+r,,eVN Z^{ixWV֒t0A|欄<3u? ա~u%hc+z1V}JvΩ_ᵜ&iCtϦ3[IAWFo*ź';Ce.,v X F]}=+Em^+ ro3I>X\c4suKTwK'ݶ㢒O^­$@Q2%AUM_ CixoKlq++(r}5QҼEi2F42ivڱ]ezSחKo~Yzoȑ; 0I5LVw=UEg}{ߦMV;o~w7Z4Pw罿o*{Uh@c>w=UEg}{ߦMV;o~w7Z4Pw罿o*{Uh@c>w=UEg}{ߦMV;o~w7Z4Pw罿o*47I4!c ZWVUqcE+QHbf5[K<(3I7~Fڱo(ɨo-1,Bx"}(}(/Dд fMvqF>:dI<93GN-`NFnj6Vڕ t$խi FӴH*;hݷܒԓɭ ~iVܸ@줌>'Tu5  Qlkl.i#'%¥}*L aHOM&TW3HlڕR}j8uW0GQli#lE"D'?T!~?Dur 7_4a->9c!ycQiw óF(tA?NΩgwfwұ]p !s/bq*-.xv,-/`k}d|e{O[J^:ؿ_ʍ_#cO?obq*6/g|O?Ltlicؿ_ʋK^1?41~ /bq*-.xv?GƟA&:ؿ_ʍ_#cO?obq*6/g|O?Ltlicؿ_ʋK^1?41~ /bq*-.xv?GƟA&:ؿ_ʍ_#cO?obq*6/g|O?Ltlicؿ_ʋK^1?41~ /bq*-.xv?RC\q,On־UťME"0 i0Tt2m$ +?AE4L+h+./s{xPh/RY1 7 {`ռ[H0ȡ40<*]j|IM橩Ƒ@C֟ !J+%Nyr5q4 la!*cRW01ڮ?"R8:Uj1Csoෙ6ѐsas>x6LrXym(yKpњWm,x x{$ ,W Aab vԼ)y5aepg,`0İoq1v&c  CFm<μj x'X U-,R%@Àk3FMV%d>;׭縟݅6cwd9c xGMV2htyw.wgqs6K,*̸E[EBAZŮpIG?kGYv^N$1*;2]2u/X>28Ιk!B˸m8.pyoJI?Z.ٯE!RNt R|"ӵ--&-:rndS5U'W帑n$t'UA"ne\scjO9SnTEݴm_os>u@4wmj?Ə99wmj?Ə99wmj?Ə99wmj?Ə99wmj?Ə99wmj?Ə99wmj?Ə99wmj?Ə99wmj?Ə99wmj?Ə99wmj?Ə99wmj?Ə99wm*j?ƑoHK5BGyA#SE"~O !=qUA7y6L+"xoR in8b0Mu{H't^vVdkQy5K{2^Ǯa;Bn`(Ɍ7뎵h;/k)#9mcJ| E).{B's C@ H;9(]nzWvJ#IHTX{NI!yS" ^Ujzt1Ays-"n\nTоֺ7 $r!wpp 7Qw}2P}-ߖז ׬=UЭJʇqϨ栵}[Kۘ%6)qQe{[LWv?_E??ESvso$q, g+n?"?"@?(?(EPo(o*C99P?"?"@?(?(EPo(o*C99P?"?"@?(?(EPo(o*C99P?"kkiTyHϵ`gZuWUh13FihqM̟_@fO/֣2q?4f?Z)}̟_@T0Z[5u1)ٓ}̟_@S$dq̟_Fdj6'kx eX~dj3'P>?@?~dj3'P 'ƏAx'Q?ZH?>?O̟_Fdjg ?O̟_Fdjg ?O̟_Fdjg `9 ZJ(sFi(4f\њJ(sFi(4f\њJ(sFi(4f\њJ(sFi(8=*xĖ֪cn$3|[?ʣԢ<.+[A3gsCvWajof>3-n|/j"kt͂I= oz4z~u!F׮4T4裗ʽ(J)Sӭ]LR I5cUwke?%,0APC}m 4l?\Cӯ-K E$o4IUgz??⿼UFˤ!v4sKf>l\[eHh߷'<+/EZ%e%U)|+ #f{Ikr? { E!((((((((((((((( SĚ"Q2kfU+o tͤ ee@q@wχ?f5<=u\]nm/U`C*NJ<w^U5a # 1 271 303 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Rc%TQrqɨg- }`*yS"kYׯekeYnBϷzb}Zྱ&\:nJA*uܼLExvMw$6XeWz֔+{h)# V:mK:m)񝬸4<.y Yjym EaK=Gq=VUڴKU߷n{rѓz~运wN7-}n,c;?ŢqE!Kz/+)kOP:|kb QY9'W7ȗIXf-R px˺crdgiǢBUkbe=#zrݹ].mVO֚7iІF9G$ܞzНǩ ]!^}{mVm܋[=5$/$NL_pp{-αsWFIV=`CN1Bw z/F?*jZĝr{]KQOz }EcxW&=2o[n]+7 ռz/Iz/+g̺ONꮍyS: I$ß EǢByy\,]=o pwx_Qz/*GE!F运^oo wEǢByy\,]=o pwx_Qz/*GE!G=KbVm襸Or&VYF 5/[ܴmlrPnEw⟕gxXԡW:(jPws:m@ 4?ߑnV)Qx?~UqWRNSwlƏ!H($OPy~Oʏc>=(HąS?UW'bSrkq=F+4?ʏA\1^@'Jم]oZ[?2CT7LҟzV}qwZO3lTIWSh?fxK.siQjZCۘɽd3VldmɾƢyO!]`)|X?4r2š Uz;y JC 䟩.e5E 9;{ >O򰹙֟٠1T}K.fyyh?fxQ3<<4?ʏAknomhYުWlFZ䯲dzIXjNdHx%B QE1Q@Q@Q@Q@Q@Q@Q@ U/t #HM _#+"ĺEܖW[&P4obgR0W/gEvF1)Iv2_z7"8TQjX IGލk+;\[J%!Ds-lv*w;gֲt3~n,jt4ZB*|O! [‹TfPzcҴ4:vP4+*-+A0 ZmRwc>}LfPٺ9.eoCS^{Kg_ºƪ4 jVWvooZ_xu?Ϳ¦_hoo3Yʵ52ơ%ϟ᜼!Cr'4`3f|[gSu!-<*SᏆoдg\Ip6g艌&p>A@oO?M>6#ٷSm_2=n†;xD ust@߶ Q_?´SٙE/_z: +xmN1OK~h{ 4}ݢ񯇗_lڢoe~fTM5^K:Z+o_(m|LzuͿ­a꽢Z^D|Ky'o𧏈=5um}^?{]~=5Q~)ccR?ƊG_tW1 㮨?',/ m[^?EmZvj:Mf6>T]lLԂ/E/Z5_ָoupRVyJ< SAB}FvK?Sߔ/M~SWg)>s3_@?~)(>O`Q] R?K?Sߔ}bp'c}+4pJ)+Tln1kR3):nj{v<~o綀q{w/?[Z0\? _?vLVI7}B4-U>hVՠq]_$^??\2clSWW-h+jS٘ T+>yߤx2Vߤ 89S^or7L?][_xxy1QJzWt>?or>H?r,>>$y*/ @xO?or<>#7bWwx5v5;LalU)~I'??zcC-x+^7bSvO;T$s8HzT\,w(aCGB:UEz|uSmb*spi~oR01U-zR|"'?'Gy vg Kg^~H%?'#?''^v, ߃#ɘWcTk("s1)?w?|0P!(b;zהqfxrSFtQEj| 2Ilo4ۻUmԥӠ]'Ch-Ώ1,lyv~& _ie[c=3CksFMB;m\O4 p AάQ,hY']\0IXUGiSJ5QFX-QIc-'uX<G_w1Ȓ]}TS~%W%ͷ"e@BJ(piy[_huY8vG<+{MQ;(_措tiJ7K>gsG577/G+{ jE^_s=#97osA9?[no_.mW}(>C~כ·ߜ]77/G>W?WHk[no_.V?UgsU\\MqF `ҡ|Pٗu#K\ܴ<9_?@$H=)rvm2J#xOѶeJԭRՄS)hd@x1ϕ_*14G~ :+3f?kQ_ߵNُ|Tl>WӢ?c+U1ϕ_*4+7f?kU+د<J7,qX(/ėw6[v PG*z I=x^"17kst̆FaNA]q;6tZ]ޣXC1bz`ִ0Cn**b1֍sVQ*(7vTa{v'&>P4h&\mO`֝6q*=d+JpH :_ YGuzo4A]W!8lK06@|c9=obC.CZomN:;ъJ*0 +Uto ZTb=JrZ&f`ӿ?w'V(?i>ѿ2:74O,Q9#=7FF`ӿ?´F(`w'Q#Nd =O3toi LͤJͦFldQpsGj; 7ePI⚊[!Jrmo%ɻ`\0:ؤp?*KO+E;4Tм29AMd}-dKoSbP?dKoG-?Tأ-?QKO+6(CKO+}}-M1@}-Hcƈ=Bҝ1@ EPU5[GP2Lj-&5[.M?dYR_V1y^Ѹ3MẳR%o*@ctTgF840ZNjKAGmm?Vپ!߻_G'CߙO^HTyپ!߻_ݯW[;yپ!߻_ݯWoӸǜ}? > z>hV;yپ!߻_ݯWoӸǜ}? |u(lo!eߪ  Bpt\q[٪)r*$ =5tr;7̫b=*^M_36c3[d1$hx#}X˿ 8y$;YϹ9s]VMg?kg4f1Ϭm}c.+o&sFh]VMg?kg4f1Ϭm}c.+o&sEc}X˿r^^g20|@ EPěYSt϶}\H mm^gjZEժE~,F[dBRp=9nLE10)c<e=q Qꑻ$ef鸌G܎Iy[~ 3msڠo]>M>;>u]f3x'm/ )p8excX= d*]lasF{ 5ef$kxR"cvit7Pt4u ;B*'c${dK|7CTk˘/yj9k;&'oSp>~K|7CT[4 @t_K?@t_K?§_&/4- GQ o`?6?*}. ?}. 9ʾɄi}K|7CT[4 @t_K?@t_K?ŽlGra?_r3G lUo #c;Oܿ0iG%HE(E(*&/4- GQ o`?6?*}. ?}. 9ʾɄi}K|7CT[4 @t_K?@t_K?ŽlGra?_r3G lUo #c;Oܿ0iG%HE(E(*&/4- GQ w_iUg\De*E(*&/:>ckw-4c}C v>R۳{YD{R +~FSMK+KIp1gI]qy 9*B>5qh)h*!Jh6CțO=ڎ= zR` W3Ij]Kt)>]1l1ӽXu颵 1CisvR9=8mv;:1\k [+x id\Hqc jkq2̳䢅rs#ڝs7Z4Hn~\1x6K8W6/%''ݑ#gi;N{Eq:ơح\[jPDg N UWTQ+ 3*'.f84$@&W}T#DҙY( | PzȢNg<Ez?ڢik$(j?IQϝ +OP;_'G>vO"6_=C|Ez?ڢik$(j?IQϝ +OP;_'G>vO"6_=C|Ez?ڢik$(k4?IU:{U MZ(EW6+r]1%O3E|nS%X ۥU݌zd6v2Ib[Lڢ(L7Pܛ _>U`tY68#7}pOWh1KXfxl-zg'ZLM#G)D>b#?h Zm؜\'*҇t'f4HHP*ԴP}BF9Z|Rӌr;8"iw=sjRö8`$AޝzP]'bEH큒v'u5YƷڣDʌW p`0< RѼgu O%Evv*pq=M@}Mϩ@}Mϩ@}Mϩ@}Mϩ@}Mϩ@}Mϩ@}Mϩ@}Mϩ-gUaHG@SFk7Ql?hHi'jpSڠ.iIpIW;A}M!$qִ񨥗C9f\{kͳAbr3ך(FeQe_'OPTl_?@=SyT:oOQ=S)l_?GOP.zޛYY y0yzʋҮQv.qEI}k} <B9 YvW]$vQHFS;vIf3GM} <BE7͋z(b} u6/У͋z(S|ؿB6/РQMb} <ؿBE7͋z(b} u6/У͋z(S|ؿB6/РVG^YYw5f%H5#=vQƵ|ؿB:/Фv9ԖE!Ү${{I%ib)}lFS85ojEf},c n6E(LnTwnAM d>[lprq]y!]o??]rEfI$GVikHFN?3VuOOFB IJZ}ds$ysۥZ8Y t F)wq%c/N5]QKl6-.܊{]kpG˛d6Y]P<Ǧ+k~}&r}b6_sR}7.At[tJk9[Uͭn]JolΟixK:i5&v1T>u@CIYX= SiI _.BZCi 9~+/7VmU>w@O]\\,(F;qs֝EC((((((((3Fh њ(4f(3Fh њ(4f(J(?fotoxx-15.11.1/data/images/batch-convert.jpg0000664000175000017500000005647012616075370017313 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:tonemap|resize| 4http://ns.adobe.com/xap/1.0/ 310 449 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?N85^rMCqY]=8k,&ddNsEǬo 7Eyc^9D#H!d$/yeK}bKlnڎ[x_v=kx_Qz/+.|WX|5f6eϩ< ?֟>$][ɪhLgd';?)>\V=Cx_Qz/*ޏ3ދǢB!Tzyq(GrJ H 8b[h&' zԗFA]C%$4sRGC]Oyq(G(\'[\_?Q<Es./(Q"P{{Ǘyq(G(\cˋyG|<ȣ.cy<E\_?Q1ze8j׉5KḾW&hBf0c,nބNM.&?]Mr{[`Ʒj[L<-!v㎙j޳yCiڬ#{ȓ1Fv9S{L:]M`߄ ݦn`yFe!pB85^Mrn.S ךPW;o]M6[;wgTD1\ſK2[M5c-˱#cgkΨiby}P# e%[hO*+}:?/URFwkuXKۃ i!lYPYZEip<`BHPrILGY1\|<д+m-9;C8V}*K= L`iuc m\1I\M5kQskxL*L_#WtF-ssEss t(V—?p~R\,VY39SJђխPy OtVx=h"cVEg ={R\C\\4b8VܽMOc>VK{hKьۆ;&^3S[I薒v$ʫgl}rMZα+_RAem tn [vP8E!@5=8FMhKww!YcW?##&hze q ܎yZ[MM1eYIfQ>P~D Fg2<36: [hh$xJS)[4_3;< ~jN{Kx rK(ORORjF@+)mYFcI-F>"j6F`ls}-2aSm,ăr*_4R<d'Gִ66ʋ@ҢHrcrb89}*֝i G~f=I~*F@ Ѵhm4m4)MM6vF@ Ѵhm4m4)MM6vF@ Ѵhm4m4)MM6vF@ ѴmǙeJuy-!i2̈4 f0FTz\W֪7e-{#VMX|=ƧTGc/c6q j+$WT8ewnת\Ǐtԭt ѣ2Fg>C]w;:gIZ,<|R!1^qji=Bbr[GjխY.nt7'l|/ xJ?YǪ^^xҼPߚD3g /vFݖd^q9DKNB׿G_{O.\!erA_{jE>׿T9}~terA_{jE>׿T9}~tf^^g`?%\ԋ}H*BH:ʉzqTR/v ?"koi\vR/v ?"ko5DH_\ "koR/v 4I%Ư$.1ΟwA"_{jE>׿T9}~ter_{jE>׿Yiv+G4(npJ5(ڑϵQ^;4;_{jE>׿Fh _{jE>׿Fh _{jE>׿Fh _{ڈ~nGxb (8iT,ҕ8AEOEUluJK4>5/6 ^,<]gˣi}[ go4 dt9opsc:e ̐4bdo2n}ݽ;ЭM7JREw>'Nkxl/7ƪ[rXkh`9-ZH 0lc_w45%!d'+m`sEi6 RM3(2ݑ<}`Ѵx TI$Ӭ:ţkK+xLd (9 Qieh1ɰPyZe爵E-m-c+9cE;w*`AWC>OoX[<1Ȇ1$$>\\q=dh І/j0'Z4Sٔa c%ԣDgdBIUPTsSu/sč+LA˨(ēcæ"a Ji IlDB X/ӎ_/'3Qeڈ&ďwJg#;Oֵ˽ROx`S9iu܈#;rOJb4f-91wҬ,6` }p?*exW ͵8Vݔ ːݺֺ*eX$vpA,}-5v\"ݭO@rBqں:  C 9|WfGIoc@v5NZ֖w% m@HPNNӝL6mmt1c޸fi9C94!3E }%@c!lhֺT 'iZz_Բ[rf$-[kryOlp *F*f1PFý_GO_LDأ?Ə?mM6?:?l;^F]jMdY@OSiXI)hbQEQKE%PQKI@Q@WyMc];ۦI9BB9HƊƖNN]<]*#,6ZlNyy[o:++]fHX?#8'=x5۳o s, s"lu<>Eci.ijB@`xe#T;mtP?F5! |k_CcZm?QƵ>?Kӿ! m6j+gb1j(٫ϞQ٫Ϟf>zgo"qF(#'_CֿO+cb1϶Qk}"1F(iEֿN+cb1~IW=3[8W=3F_|ElP8h\8(Ƶ>?[k}"u[K[Q+22IFp 4EQɜ :}:~tQq-̐6RFl;}cHcZh4yZ'/[X8^Xr Ԋ؛kHlghmf[}6ЪܰV<4yZ42x. $&Oz;YJf\X(wԿ畗o&^h+LѶ$ LUxĢI6.66ѶBi9o3 J]ߖ{t6?a7Ga7@h\^nv*g闫ZOԑ*3!]l>Wk{hXaz^l]az^l]1hXaz^l]az^l]lmmc{t}{t,af]P鋞Ur=Ȩ;u{F3F=1?%?I 01h`~rOEմqyw1{P{zC1?C=y}W#&@(% =Ck LL"f$6 :iS1c"״_GM?a?@xZ-r]Lnwcp@ہR~;cwrnxI{ FlUo.o.3!𽥸\^J弑*-R:rkb30RN ~uM?a?SZZڹzF #鹉?Y4PhR+[e@-y*sCG=]ghmZ$޲b~rzOJ@s˱r`ͮ9FgfUbTnHe)C&Oy M&=yt @l{ZB2_ ֨Zkyugi i.cyO rxxyu T;QRۗ7[~TG׌iwi@1 #kf7R۫NYvslΒ~5Ʊyk&K[Xcy`r3(SMQ<YwFce>U; KcXX\"xL=v;WYeQG_G ɝ8f xk#ӭ㳊i\b.p;`=;OmS49 $i'cne+:%I\f [Rxv=B+I-=qlY 0Hֽ:(R(#ETUSL,Qv9ZC8ۿjV/-,%[ H_p[lQ_6(-i%JF289ZrhlYS-s1$t5uZ[ʈQ B$(t Eo5 S%ȗ|§j٫T*%8?@-ij-͕ >CLޱ[!K.a*ɺ+ʀ7HqiЈ"ٸYf<@~%mWZ<4E"/]>NRu+ɴt{M5n$qdlnmæ=+6w-D w2N̚eeg$R[[o?gBGZ^wv[QPI$ d5pm4]@ՃHȮRxo)/5;JD7Ntⴾ@4syS#dS;t 鎏:5'`2NGQ[ yѮ졎4SԟZFvOW}*2\ťhfljڍ0is kkY-ĪY ,tsZuA z}twYj?hAXOxD6SZm"8^MԣV@7"݁I}z`+cQ5Xje2~0a"A 5zΩٗ|-m Ȫa'AJ;GO(=eu*xF_BT*ڦ$d}S ?ƏG־6#bQ5:%յזf[henBP%[hJ"ȧU=@Zgz ~;WgMJ_H/Io%eˑKG G;R뻫 c Y!3H!Pw6ژzqzU [R-C$wPάg1?g#_qIV3G=@9UEtNXhU"N]Ю=.kS`(_jGkoj m\i 4JU(<*t4K׹e6:ȸ0>vO;OjGO(xMgȒ@V$8WSDscQ4}S ?ƶ6Ѷa+:cQ5T}NklgE2"L0APAP+&bX?DTnu:%ٵ-J(1vP Enu6ǚlMYӊj흸*MH|{բHv=ǰ3]9>g:mS)D9ky,|E(T瞄WWs٣k7Fy0 2|u먣[-ڒHn]j(,O %-q*bOZ$IJטn~E:Wk>oa f *(JWsAka$[I"3UbC*\#]sph(*RiVy2OV\RRE<#wm#c "ѴwoievS[ ~#[ ~LxR.,f{ib_͔ 4+@ZRr@yhz\Ϩ4Q;j#'?P0洱HH< FEZccP!h ܔ2#btr;sZ@yq nI9:|>[)d$V?Q@ {b"m'_.bTT0#mbmѩ!5$;4?r)?ʙ VyW(qemi%^E"%W*Pp{z[EHXS@Tl^:)?Q ~gy++ĪRO}ߕ *640' daE~uB#_quzKf6 #mov$0p8sT?/T[[ۡ&HŸP4Mg4PFzHe5Rm[%2p &iA?eP}7_8(4p[ܭTW?6,@V>Oɫx8 I ԚlMf(}'T,D.EYWbo)xG@V]I ƯtdvwNAѬZsmm y@$y<)?i~W++^|9E"3}OI'hͽ'I'|Qdd,HI'ӥhoל l{WWkoEqkmc4t"BB:RHw*tgO^|Oβ/Rgύ_35󬟰XdYJFZxW(UHGRXVN,wG=taрDLl0+gl;TV 1+|-Ƶ&ߋi2Udk8V*Ӣy_@/0fAgs=kݠI c*}G*Z*iх/_RwzUٹ7grAm zG,,-l- *K.Kn'$sV(L 4 2(29C#[rKuiO+n (YrGqW Z.dDʺIs{k^TH[X-\"F?+34>rR#.7@l{։撊(QPh e5Adk*!r38~gS.չ&srIo$e]n?:cjpX#A-7n= 07Qj$u& +hm$J9u޷ZX˭w[t)'/$O W!r;?J5ZR?h ٙ\'Fb%QemK#yK\sM}27h50 o\I] .%k[KKǎLm_-]à.f~sc&tK n`sٕ˪OeXF 2}*8,`#zFScB"zŊkKsN!Y]#M7ڵ32'Tj}-5 J_{ >rzvYeq$HBs]>gsv,j!8y2Ak7OPP6bmyq^=A52 14`L☶Iv9DKz3-=Y |T8{W~VBTֶZD"(bRx˖2J3r͞ KY1DOMX5i-򄄹y$s#P!-6}$w^:"02uyN 6K$iboyc6%bϭwsXZO:=ݑHWt{;K|ICT)nsjiȵⲊrFǪgÚŵ2YF `*iGa-7vġ3Ժe:nkcjmX{ԕ{#ʌ"-h^f" Żzt-k7WWlT@T F[k!?t J-\S3Hgm^ZkFH+̸HcQ죂}rk+\ou %}S @|!=^tZ =TWbV5v%gt sqZ] 1J9 r+rk隌 Ԛm l`@UFU}FbK9cc /\u+$؈F63#V^]6KėCn@^UuQ[Co5s5#6ERQ8#i< TsN% `xߎ4TUFT`H}Eb\QJ)qF((b\QJ)qE A u۞,@3WCv~^D_zګDF!tFUc+G uݧ]ʱoi|)$IvnaEt5@Zlʃ,G8+V=OhorfP͂y#^͵;+hn-7`@:1\7uSX>&"8˹P*8<#қxۓyIpdXy[99(z+;0x[{2`X2! đW_\IwZ]M絝d*0;ӰkQE(0!JZO;6#|~l Tvw6*.wmOy|:9jlw |IMN{5"ym8Vf 5 hD r襆>P;v;JIˉ#!3\}4M-h5;@TVp8FߧYY{绐#qBƭ=W7Zd,TV?S/5j(s&Ucny\-Ǖ+nN*GYw2@gP961y"TK qӺ5ݝai8U(K(l{ZR9K(oًf1=@b}(Z^Oo(F"R4d(Y 5"<|g3?RG*? ˏg,?kӱ|+;7㝹3@i)i5/?~}IRskD<좼$CK]#VYf߽qހ;UGRl/l睮$98ȩ|<5k'$K' فxsҖAO^o@(o^M["SƓ#~܍3+luvdXRLcbP?[P3Io}ujR]iydq{_Z[ LR)VRH$P5cmuE?iiYryZ]ZEvI.p2+ne>obO=. w:ç4̾cGu߭'+ܬGc> 9&ӧOZ1LJ5 9VBiga-Gb'_hOԿ -Eo-^5;ˋUOT<"K5Dž,$w1*,b_i5.X繭_OԿ AZ?(]uݷ;?N#޾oٟnF344TK 2^Y* VPSi06lSs\S?Եnٗp='&?<-'Ջ߱yػ3c,^}1n/v[cq TKk"ܰyfEw%bhO4H)2"b@K}1Mɿ14,R{ۉc<!?ʃR#cdVB6# LRooߘ7oE 1ⶁ#'%V0>A dq ooߘ7o6+x!@ڀc=jZVAl.5oa@ooߘ7oDV2Z,D`d9?NC3Pr }y7?ƍ<@<FM䴶o6KhZ^>v@[ԫhPh gn|돭&M{&rZZʨ[B꜠h {[y&=-(-Niy7?ƍ<@ G8cik E: Q QB$u8֟~chɿ14z|VeF* }BW[Į0# {sS d#/'@-^@z?1RCPıqqEQ? _6O??Ə6O??Ƌ& A=_N7qG'G'Ea4cNZO6O??Ə6O??Ƌū|G_ADږ:lx$LFGN6O??Ə6O??Ƌǥg?5+G%,n$nj6O??ƛch xoߘ7o2 kx?[_u8cz}(ɿ14ooߘ_C^9b,PĊf}><FM=lb2POJ%vo diy7?ƍ<@_~ՉzǷ7z 2<˿#⪚xN{X#MyQb&V!|m9'tD?f7*⅙Xv1˿#.?v }F|r8RSF&HGʹP$t%VzC> RhH*ޣKúj B6I2ɸcO9Ie4 qGH vNf Q]6GR,Vp4V~jW 2U`Jm

    G IWx*~c܎V`Mk uF#/}/ݿ:aJ]j@GR~a؟Mt5=D((8(((((((((((((((((((((((((((fotoxx-15.11.1/data/images/plugins.jpg0000644000175000017500000003074512616075370016230 0ustar micomicoJFIFExifMM*JR(iZ0230#0100Fotoxx:mashup|trim_rotate|tonemap| 5http://ns.adobe.com/xap/1.0/ 300 1000 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?4P6^=O+M`OL1WsHPA$o:: dr^*Vt: PZMmgDOm#ڲvmuk163^Ǩ]11IE5%QpT&t׺վ,q\P,eAM]-xZUtN RZl52F.qR%;{dd u}3@ΧΓzvQ֢¡O5  XX1MrH☮vbᙈY)ðA: SC$iKpH8ϯja$FoFQI_ZC7<hQ@ϗE?ϗ>_e>_<iPFf,I;ߓ#n84?kTxtvWc$ͪ@$T6 =eo~n0]k+Jer +OcSo]jm5z+^IeTca-ؔС9"ǡ4 )crq)jΩn-bOvd{*]5,.o-Ǫ.=/oSI$ GR=N0xIRNFqV.K%by3EǨy<;Te6/;h$)kzl嵅4xI攸\,vcd([Ƹ+ZN{Un H˞xϽ]7WFE$$zQqX֒Mol6 Q ĉu$p\[ Ӛv 4d#ב,cآ6E Mm2xญednkkmci@ ԔPZL%QEQEQEQEn=oWGg7Ѝ@#i|FrʈOZEp-4E#i|4t^sVCg89' 4E#i|W3w:Gi;7 4E#i|,(psEچ=kOQ?Ȣs h:#Pt">(;zetyE|OSOQ?Ȣs*SBoY'?:+kFeSt V?K_Q`u路i D'9tk?]֯,j/0fP`u+<y+<X.bM铙 UErX>~ԓZ]ȸYW~r^٭%"%"̫;{M>[YD&hy+<y+<X.g֏0zևGE=hhy+<y+<X.g֏0zևGE=hhy+<y+<X.g֏0zևGE=hhy+<y+<X.g֏0zևAXX.P(\?E +:AY̗<˶_"MAӟZ6/8CGn ɉDoݩ&kq&n'I KU2ўAH>]pSr3LFuU>ƸHCHVE OB D41qq]#۴m `~4,ʫs 3t $efVYz P ZxmыƀE(=I@Q@Q@Q@l4OOy=ݧNn9YvƹuOkBݮDi|vܶ~OOpCQ/%E2iH?2R1C]]\\$wZD쌝ӏ}ׯqF\_ ~gş-<ڎe5l\̶Z:.$G RW uɭa x;y7l#~oitثt=յI8$.3aSl$p{G\5>ఞHU@6cҀ:K/%;#LЫռOZj+,6ل#5,~uo>r:|R[̬s4v_ض_ݟdŲ'\W]iw.,lq^o0# /֟}g^1^Nib m,rϟI\1=jv_ؖ_ݟdIJ'\MV 6w|?kqpT c2)ڶnm['$- G[,?Qe"O*W]e.$QpGZ`Ե;jڪ2.$Il};[/T>LUqR1(./y`0y zqc?Qc?UQc?Qc?UQc?Qc?UQ:=OcvojZIWEAHG++'JM-ODQ\Z-$̸+H\0$2 6Rd28g#ׯ{T/?? j\REFM6H(nGZ,+p2m6Icv@sWQFM.CM?P+M;٦ʸlsCκF hRg L;Tyc#|٣,ai]賗d2I vr ֱ x.l-ӗB8)*s'U,%im!F̈4ţߵVw"9#TC w1M}--mݏndp˶ެiuVa KŦ 5ӝ;$vT'+ɥ xz d8Pj/=h#۸!(b (( v@⥳F)~FMh޴(wZ]֛E;{zѽiP6\P2oK.3 :0\g ʏΧЇw#ݤi0iphу? /GܹY0h у? 'Yz07gR cGLkSC ݥ) P$qUQk/i0reh`у? /GܹY0hF\,M'ܹY?\0h>._&˟reh|ܹY\0h>._&˟o8w<Bu40ijV!W_ 'TW}(Wz4P{ڧ9J͂il!)uKgvAXߓq·R,.'h7I< O({Ru=JYuvVfUrpȯN Z3Y3C$Z̄8c]zU[6BFsl>c=?i- "90=@uwQ vq<0,yh ;Tԯu{3ldI Q޶Bvr$6 v˜yt6k%f[cЕh@S#77q[Blm'Xd9 ;wIH!)mQzr]NI g$!BW4C (e3vP\_L[nnD 䌷ڥ^.ֽGp`tӾa>@mn99=AirL ۤ2\:j;}wjKX%Bw\u*ܚ{h~k1`yR33m^sTˣ%A2;6U92W4f{,FYUzxQ-sol]?,mg AִDUi}jMNIegc3~}y-L0HN>u$U*kչ${YLO圩9JΰʿI,$\pGZԳhf,Ř1$ g7ЍB*k?j4LBbWkFKMR cqk NsT C޴v̳'gU(FH<P1\,$M-e{ l$y5:ٯm mJ+Du{SvأƷ/&[]EU d;=Ԛ֥o 0suq2~w8+tee=W*O:Ow$@) g5*IC~jb:::ZU1^Zhkxуn 0;i>'Eޗԭ$[(aP q=hC?]?-u]WWIj'Qs$Sj_;-P 3GԚƒK?fyWLor*#$g<w{翉dHc聘c1Nh:H'P5>57VWq K o*4YC*rvQ@?/kh&B mF-FY,YGL 2xv8"H{E.|廵ws{-=IWHUd4GQ{4[RFSK J4ITLp?斗J$'Y]Z t*R(I\F E:a"6ДS1}qND4iڊ)hѧY.AϽIJ  D oa,@0OR8ͤ4Bc9P`*mhަmkM5Ьf3Tbm_H(xWɌDAGlՍhަ!Y*S˰P6ԏJizekS$`( ަhbA2PjGBAS=GN>4ooS@$<[&ᵶ(to(E_,01SoSF4}`a1- +m8]/FHIy?bS9\"U}MnoSF4\>0@r=:SCncA3j{z7ZIrͻ;>X8clD/1>64ooS@w@1(k-ÆxHr;IMm(RP|tTR?.4ooS@ c?)7W]°LRH',OS@hC$(|Q_jX>M__ >M__>M__ |U(?+}WW*X d >?+b,d EX_zG¬QE1 [rq֪䎊OzyQ:CySo1Fg(yQ:ngno1NySo1Fgl,J 2Is377?R}4n?vgno1Mu>2i4]3[@dGhۛyQ+D, L' 2Yz?BhۛyQ7u}Ѻ|dhۛyQ7u}Ѻ|dhۛyQ7u}dTrySPr5Uq4<(b|dhu>2i4<(b|dhqVrܧx$R>9XD9VP(((((((((vQE!Q@Q@Q@Q@Q@G!՘ RTSqsh}%?PwR|9fo&*2_cYnhncA:f/QVV%}1TcSnAJfmՋETrx#8z:RǍ!СmP"4@ϩ+{u C{24 #OkDڴlEVt εIbXÑ߅V]BŤ{:m~] W;ByJp}*6=F^4c1`}NjJ+]'5X&o10f u$,͔#5/-E|c;uk2ø` *Q۵ 'QX16.uLv-&pUdH'*K p#K@gGT$iK2[K'p:K?R^Vg4*#>ޏոFl+$ˌR}*֕sU=+toY.srтĮ 9 c5!tHO6)gp1@lmU5CxDB1Y ew65UNj:cJap{mu9l ų\"N%F@2s ԩm ΙRRU; K_[58bXO>[{bY `U=ļ͘<[|K) z\՝+\UiG,es8=v|g}[SgFyU#Dg bz/4"5)iQEQEQEQEQEQEQEQEQEHt-?UyiRVO/@h^Zq*7>7؟_ʍ3NN=؟_ʘdw__E!geIEPZ؟_ʍUBUsec %P\N´'bq*f}o }o ?SI'u|EQ9nz ֞TlO/He8HbXQlOEfotoxx-15.11.1/data/images/edit-any-metadata.jpg0000664000175000017500000002360312616075370020034 0ustar micomicoJFIF0ExifMM*bj(1ri%gnome-screenshot0230ґ01002015:06:23 15:42:25Fotoxx:tonemap|resize|NE http://ns.adobe.com/xap/1.0/ 150 254 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Zc%UU#VWMO֪铴xGMWY|o`fv _S\XOi%K&Gq/қIi1*p^'J|Yswq* )tjX)WM4ubFݬ:.3FB:Qxp܂0O\ jԯO{h%fa"bp5:{L9KP |cXc.m.;ko{运x_WZ׈<@].RHWA"%|~7xF7MKc(` % xӸZ=o ?jC5NKʥ؏/?)Bn̈́m:c r>ͥc>ۻgǮ)Ǣ+^/6_;H!0X`گuC¾ /!9q${p;/)<运3&iVk֯psg:*p=s]7u ʪM<{/Lt`_QEQ`b=y!T|G=hXE`_U0zZ./y!Q],sA`i>F Va#W1} ,sKcȯ@Ǐi|<~BV,EJ.ݎoJDOR}?h#8ˏy(i|Udg)ij(HHY 3۵oyq4\?!OW0mH]OjNzi <~BP]xS÷w^V]HcJKdZΦe) % #ˏy(i|9B#uhV0\iIU`~iuVIdѵAsh?J<".?ȣ.svv7Z$E#( *_^wwsВr6.?ȣˏy( N&5Y,`m@ cFӼ=i/qe[+#0n<".?ȣ.q>,4Xf)p̧RsALHn$ɂCMv>\?GOQ8|3, }.@ţN~Rz5cVQ$6 Q[\?GOQ1G+gˏy(i|9B7(lq4E\?G(\x.?ȣˏy( pimyQ4E'OQ $H]1 d>,xsJLG[:D7bB\j=E-E .KhGr{eOjO`"߈tO/|k-"V_lW]SVÉ/bOxyjF^ _0kˤ~Yx [S/.-uoda!}Oz˴׈n$ݏNL%qcѠSGcH;~׮ja?1)avꛘ\NB/|AmB -7lck?4a?ב棨.T<3Grf1 q"BX#:}(Ԉ"8Rǵue{h]01 >'4RxrU湒q(+Xʬ- 13؁[3"L%EUaihHI@wc}i m#-+@\ R=g3,Y ߝIK@X\eiEm s{JfѼǐo? G-8核lhP@,G? ~(/hzwH`/!==*M,&Ь|_lMUۺ7"ߛ "+o$\b8hG8ۺ7sOoޕFc)vX= O"`秓~xRۚ7p-:V;pK8~}IHXAV.q֩ni?~RϣHr#0sT:|QpP?Jۚ7OQc=߷ ?toH ?KR{+3sOoni€4,:K?~Q߷ Գ @R["VBt Uqi8W%^'WF2@̯~kd]]h19'=RK&fF[N+t}hvvZt))MJ"S3rWeѴKhWҥJX`JC=H^ْ^[ W^)^-kuRHdX_l<7N#lyc}CrpyPG"ԼqZd*Ep[ԸԴa)Dd&UO@rx5K+M[E]S1DjsӞ'bM;_@mn6"i$YKӤOщ1c7?xg\M;SRisl N'ӅƋ:5l8F;sB_Q}WiI98'qF{: c-̌4p3 q"x;Ě}iXjRii,E9YW uؖH{ItHٙ(@\sз~,Sq[6FPpJ>Fyy=[y{6 L486"6!ێCG|[wOSF\=^FG g4}>qSv(F}:P "sj+TGξ;S|I}%Kn&mvp|8'mS%~Tc~U&(!~Tc~U&(GaQaT=F=RbPxI1@VHưa5ƲOcr Y5T qM} C$<{f} <B$5OQE=&h΋z'(} eO.hJrs7ޖS|B:/Р}j>}k7΋z'(} wo-67ޖS|B:/Р}j>k7΋z'(} _Z~mGo-?6} <B7QKOͩ<B:/РM}jO:/У΋z'(~iiړ΋z'(} _Z~mGo-?6} <B_?Wn4h|`]݋KE=uao-4f\њJ(sE%B@'UuF)$Ȍ=FzRnJ1WVAƓWO_Lt? IktpH䔰^"ue^[][  "X[}/?Ə]?Zt۽ZLGEǡ5_%m.Kʓ\jXsFax+X,wDz uj2?w,gс+Tg+?AF]s릎? J-l'Ҧt5%W Ow'e/L  qLұ(ұ+UnaeܳFW8aSx1ilaր3"q \\LHG]Pjgsy[:[h[l} 5 _KƀڜFr8PIڏD'hy7AU8++%7)n1Qf>6j3W5O ٿq[~٢Ddvs/j6N>a_hy1l\bAkX.\ԟΕO̥qڼ)yj-LKvϭpz[-r;&O{ _22\d`JWjBX$h@9UynMu|G ݊Yx&U 3BB}Z~#3\Ek7:۵ A*bD UPvppN+|W|l1f>6_jX~9}E,5=):VL?ZS7X`Ҝ{W۷+ĭ6ZGشqsw ?1S!X;W/_!Y`ѮӒ at$i0ۋ;=+Uu8FY*+4Ǹ Ծf>6_j)խFMk 1 2 3 0 313 288 1 C    ' .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQROC&&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO9 " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?m`@=yI<֐lށ:ʹ=vYTV*NjO\ޅ%9Xꡖoo} ,l2+쓽BXRXϭt$G4Htx9+jjd5`QFEcU% G@zX._ͨjI`ږ(Vry S67eirO-67j.s;FH\ ahn<2ۜ_rɿQoFZYR'2\cK^DG"gn8urɿQoՙ=^ZpA|2v؎R\M`3*}pd~H|uqX.lly&mYV@@fa Hۉq=kSNwq&q{HvGZI5ؠ+cZ94Iw[IС2Rnqc:Ռњ,BimNH+> JƲmCLњVm&A(%\޹M>+{HJRc_RrGW3Fh8XүTu88>f (P]FSV3Fh?9/4y٩3Fh?9/4y٩3Fh?9/4y٩3Fh?9/4y٩3Fh?9/5ԊWk7T#XW?q\:<tTyCh@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE jzPus4?RVQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQET뙩 CA?BFLV~vS-]-^6P?N?t WF(A;_('kE_ ۺ?wGb(nvUQN?t WF(A;_('kE_ ۺ?wGb(n.ilɨʜw\S&)hdeah6ĺ-ݲOVQڦ'kE\b55UF(nvUQN?t WF(A;_('kE_ ۺ?wGb(nvUQN?t WF(A;_('kE_ ۺ?/M'PLnoeHU;X຺KxD h 5QM1Um8ڕ-P6@hqɦK"YU*I?٭/P4`_fxG٭/P4`_fxG٭/P4`_fxG٭/P4`_fxG٭/P4`_fxG٭/P4`_fxG٭/P4`_fxG٭/P4`_fxG٭/P4`_fxG٭/P4`_fxG٭/P4`Gg(Ve 94]R#huWi'@0hs m RLQr0jA`dN%8Իly ./ހ,}@Dij/, \:d6Eax=Am-֗k+@MT\ |VGe}+eBk<`}im4[4 M&Kr9+0-tѭ!*$@bqQ An$yl~cp}5@CL7bW\8MVlnB>+Ri ZUgqb3"=ZfqRj5e1ͰD-:=jQOB? RV9ʩ+,Jm\[qy56m쎮AeAҝr2 P0CS#1蠓PX:$sȊpT;ț~ZIRgY#BǧKfDBHʟ֘ț~Z<cԡUEr e`㊒;g q3PX$RNWdt"̳$Oj@csQi|菢 lZI(Wf vB w77y P .OMp:椶劤ss$LTGp"?RR7g2d)`X31"ț~Zi?O{3"?&"ooi-ݱ&]2:U˗j>KI9)hKBT ?9Z@G#?QI#8)|C\iUfZv\ 1b!c#>77y QoT~`SX>ѶB1ߗZ@#gϕgϕl#<0'Vog ` (=3AU gؓeo(pT* CU???tws c֧!8Ǣ(%A+ܕ?RIH$Eu=dTo~XQ! Z>|أ>|ؠm 7#|أ>|ؠco*Wlң&ú/e:T*#`܂ :g&0h~} 5P!b`c'?o~o~Xҩn?QRgϕH4T_)QҀoʠ-e\CfY7X2]he8e3sFԩ)Gn x2K\߃K[T/9j;xV W$l{uP2L䓉\RO+D}B Is|R7l*~7QYpK57`f9,}MX9biIrPZ4hFjķk$f\mm t6m_y\p[9֩%^37lEUQP Pbk`HŗP;ci7%l:d N]=rzD٣[).CZ.PMWI`*b db X1¦p_#U9-)ibTԁɠ <4aS9H&[DxP[:>Yfۅ8oQHg=}[/&)EGJiZ<ĺ0Fc&=# ֺj)4W`ZWCd[袐)6c3Qr<n(؎Ha#ЊPk7Hyla"Jkf8{y\,}˸]=s7rI14& ђU`pA䎾}b"dەҮSQEQEQEQEx&F?74U%eh4崍eC c&=iq%fݴ`1tEc[7wgvbQG*_j˯ Lꨒ+3>ݽ}ց/ZĹziky#ۻ ~uck?2;\|#AI\ݽV0Nc#^kzծ\3\,hRܼr4bE; %Uv(950T'D$ffP}w!'~Q4}Ok=ȎA'p[gHdY-ZjT^lOihy $U:s:G<;BWRhצpdbX@8hDs3*Lnv 5~i Sm!?8 h4qGvr-H?gpH'$c5[xd@ (i?KB򋗓`VE\`uctm"GJ͌{`*3\i7f۸c2qBtJJC ( ( ( ( ( ( SOHTSV"ڡU$^3 300 363 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; C" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?#@GNN:RjѶ`*)+g+/P痚۔W5Z|?M63HHLpvMsDPIJVTIP6~59ǘƵ/e7̬H۵Ueada**#v ݁ʨ^,MUvâV5w25қoVE؞b 6yPv OQ~U֑=KPi-A,! ̛ӯ}wwdFiwlUEc7{ʍ~Uk֨F.5Ym\eFڧ56l粊K{TYc,$$㡠GLF26ӃJvCP5ckfd2x$?+[\m3^ltwb8x[.gMga l29hX-~ToF_ѭͮ UvdqǭSojD)F{,2#9;~To6[:x~ hmt,xlq=gGrpGhxǠĺ͂Za8[Jƿg2'GH|M6Jڵ7)"1p3hXE"M2\\ZxTխx->o6m,YFÂV<'42E08$;Qpnd~TB Xq_Os0ɳ}ZM#XwR[$(̋nFI=?Zvq&8ӷAWhZ}:NR{JxVI+$*Z'KFwǢToUH oܺ<aKw zSq7E=Ez/F迕WF.*7E7p,oQOTWF."ŦgbO@J*I*yP @NkVLd rP º*/ȣʋyG|R#HU7xVmNyf=M<>CڴȣʋyG|+"%''vgrϸ* :bzZT_?QE<E; xf,YK'?T_?QE<E Iic2hݲʌA㊊InɹSvfFmyQ(GX.s a6aaf?1tK\-$Y$QdƷȦ-]҈c_W ,9Ҵiڞk1$Z"6Vb0}x"Ӧ{OiIcͧ!bGhX-:YJ)a^⥾T'2NԮsE<ET_?N¹hP[嵕ocxm(=}]1GFkV ww"'lyQ(GX.a[h5v$km1G#CiNj-$@{zQ"*/(\B']:'ϔ''9}j6}vv65''ִȣʋyG|,9<=_:\ܫnW\\gImh֚-#$0O\<ȣʋyG|,0C^[V`0/ &Xvy6~E<ET_?Q`H hE]Q"*/(\*WI 7pK,_}@̟POQM5$^F% 4{r03U-/{ [O\G**GUxEm~.GɕX^x7ڤV<Uj8kDžӿ4{Wq,vǩiwt-xBUr O† sSJJ!{dc3.H1JCݛQni̻q5]G'r qqtSu{X5HlKIn!"@#:K}l s]}picroL˴7s>.eg6&-Ri7/W^BTzG// iZG/b`] REzDr$:8ʲ=AKPKP @~P@RiҤѴ8]H.NpU_˨i¥; Ur**]- fsgƞ$uVvl13_5:Zޚq !$ϡOoɦxHeK(=r=EhxR[m*D(Sc` 99o+ӂҶy\̶д cTBY-?ȡ?gSQ5uR)Mj@2ycg*[NgG\99j \\]BJT&xQSQEQEQEQEQEQEQEQEQEQEQEQEQ(4f(\awAkg"Aj. g䍪1uVNj%]GrteHKuadt>%:}Ziin%w03Ļ89#RT/ WO=j(jM${/_1~x e0Aڙ:T5AJ%h"iG( ~u%Dh ' (Vŷ 6sR\ވJ3Wt0TwyLbcwBOjܢGM3& a)/pMIR--z *ZkV̿+)vQEQEQEQE(_O/ih>q$;bGCZj#.Jy1vD>0Ҿgt_IQYʩbAZW7261@QEQEQEbVIo<pTI?`+>lgjj1UmQm=6o*8>BWnI?h$8>֟5ٿ?MRz1] _[$֑lz}ݤrE$(F=[G22/T⩈AW{_tU!-MP{Qm=:o*Nf&+) @qER{v)1rdH {zB S4sr&x81r mP"=ۑ2 VŶ7Im=6o*k[p,-E6$o-n|n5u%m&\Op(2\bSd-3_u7ft֒FZGHiC@7V-|Y0@֗jժD۷ ^ѭMQm=:o*-t;9vKs$q8,{UQ[kǤ[4Hk?=Qm;OKk˥@Y#: 8?u|F@qF*,XLۤVtf7b)/*Ν@ʐXW߽_?Rbk "=@5W{_lU'5ٿ@`k+FXh,8m3]_?y FtG :],L}Fݤ_il-ͻ'!*-}j6 ŭƒjv$yF&;?{QiZoLmBȇ"Cqg >i#Ǭi=͋i%BP|,{Wak7P=>{/ T s[qɲ972$I_3֮K7;Yrf-{k0Yb93;*3E9@•yD.ˤΈyl䋍vs{8 jzN2[-, 3]-A(;Qib\׻1iWX2((p| g9O g˟Ts\)̘wQ,BX%J|tGu,+_3jz+Do i b\V6iZo@1GG?+_m~) )Cm!$4vmn|ϴ/ɜ'>E[HE)lr@=i6s?Rh~Bǣ5B?iqVv3_B[-a%ʅ$3tc{#KM px_^X^S\\4iOVqfޭF7,zQiNX.yn򛋳I8, h}溏xrxgvCy{7qh%{Qi˜g&2س69b7m'k|j6`y?SSP!>w-[m2JKgw jf{0kX& n?{%"'g 6k[i[0eF@ܹFOb{ogݯamyc-6P4ˈodh0H'PCFX72[Ja|<=Rh)oj1'B0$P#gq4n>n|EZ[[OB\[9޾:<[ɨD$UQ3KqyDQ>@YP״A-cYzTΜOк@ /dSіEڭ@?Ƭji]Gi%ьgʌHrxL`mKYuS{U+'LEB~~_'V[Iiaus<+5  lZVwzkh-;$r =hڄ:XCjuIvK0x9`Choߡ5jPe[{vqʨHɢRvKK{QJ@S0?[hIMͺQL~ӣ!y9#sFT1JxW{Ȥ)!Y3*j,db,d$bP]'M9{i >'kKi|%?m|0{yg7 ~XQՏYFL!"C+g;C"$+ƏI>W?WNʱ]jPA(x ^sYGbjO GtϽi+[FIDnVRC\^g ۅiP=y@$j@OѸQ@}h}i(}h}i(}h}i(}h%QEohe#,/Բ8 uc^)71y7~ME!SWeE A=NS{ɵ ՌpwVGݝ]S$~#2 s5„ ![4d!ơ)_Z s qVqu4G˻B3ZX,gs]ş^EX5|)9PTx:$6V&(yyS݆q)V9_ѤݥĥAef* ԆlH>f,k4O !A{sHCN Ps-"}F6"rH!s{WM/iOG7.>kBʷ ;(q8; L݆Nrkf~sћGKxVOԭo5. Xc+WJG@p:I۾q?];4U2!d*\dRXhzN#ɧivvnk5 ap9A7.-H>⣼so}i$c0P|q?\?4{,,w{iRCA})6H1Fo?]#C,~q`GLMNxyx>?ڞ@FW\dƐwZ淮Kzڷ`)(W8oT]5%C2AIX&ye+A5omWwrEfNUsn97 ;T[ʴWv9sNY 0 A>[~^c5PYķY<"9Bzz4^XHrIaiEziծJ&HA|g#;P^1fq^Um8rY\Pdb?CVi j\BxŷNin&h^О5(`=A;hz3Oz~wyH 4uMCi 囋UuI}x,o/:mܰ&6Uu '=:/}Uf[teu0HRKY"0yi*U"gtOco5p*$J'FOrkwH# [b1!^}dCgIeni$Qv `ú`t KA6WXdЀ%U=Cb6zTԆQEQEQEQEQES&;xqK=~v+/ B I(b (1F(="~J@F( q~z( z( q~z( P8/V(>._*^sGF-S@U *`EPsCt&M>r*ox>Ujm=o6o*o{Ujm=o6o*Cq$3V0((((((zvRjT!*XV ʑC{s]k|C[jzZ p=0"ENwGvs#r@N1Ihw_6nm-|mq7W*8ޙmE'3i-|E xwS 6.hZmъyVVž$},(a===Oʴ?e6t牮!>nx=k QbsnxѶʃ&yx~VO$E,/tC#x{{KNqv^]}[ٌp!;c]eOb2)$PHt6_jCFռCuflB|-SVu-2;Ygsi@ֻ_*,G0FȪw=γmy=˨]#*%:iedtQ%W=y=y_p\>/lOo ʷJPn+*&\uK=Yi~lm1o_jo;7ŏt2NZNrǼ_E!EPEPEPEPEPEPEPEPEPEPEPEPEPUay6!:dc5b Oj7 q}u2y3ԗQh7 zl$(~hh&pQ[LIeӉh|s>ˋ_ݤIs2¡#Qz -| >o>*ŴD})\ Tקl))Hp350DaTzZϽmQɽk#5Yxmp"B#kTܴ1(5-?HO vҒk_\钋u [}}ghrDĦ朲ivvXdW +|rzWo>GF@cdhPfotoxx-15.11.1/data/images/paint_clone.jpg0000644000175000017500000004660512616075370017044 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|tonemap| 4http://ns.adobe.com/xap/1.0/ 314 404 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; U" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? ;h K,k,(b\g8E[`=E;V5X3_+%}2#8ns^\j{8Xz>|\3NvA5T-!lbb:[d,eg9}t]K(Va-JG@hS+C$eB-;,#|u(u+CUKma5^Hd7" >S[Ԓ%aĴghRh%]۶nlgw=6bNE496ҟ$VqFI f1y律qjm-QUf7LK O.n5[ۏ*hnOǵN6ܚW-@,.Iⷖ)uE!Gd4Bre |4$:aѡHp8;9{Y>:y例Hj\6Ӑx S.:ƗKKjdY<U/ҙ5ޑn!2}"+nsہ\MB뷫#s_2"1N;ɫ gϢig3D(;Hj6wfhgh W?#)s1mzw7NԯTmNU'x$pcہI0;ϴ?ϴ?¸_jڝ׈;LL8ڛs]}GLFGLFGLFGLFGLFk>ߵ >k>ߵ f7k>ߵ >k>ߵ f7k>ߵ >k>ߵ f7_QZϴ?™"}ky_o}?G?™ϴ?EC~S7Ѿ1Ō=Pw(ei\9կoj}ak16ׂ+^ПA_*lz՘)O, R3}"IC+=HZ> *%9IZkzG * M֍Ɲ'@NKqqs;oFʴwZ7Z,3kX6.RW3Z{>X.cc Py?O?_\k8yY13h~sQ?_\ſ]ϋՠ ~sRo>kdzZ|S ޻$x ʍUѸӰf>y?idEf*7CVO&En*7CVFEn*7CVFEn*7CVFEn*7CVFEn*7CVFEn*7CVFEn*7CVFEn*7CVFEn*7CVFE51)ozocP֥-{b=JOQfױ-/7,qHP(ө-@601(?,Iky8WLhc"[]Y%iQl{ר杦ys:Il.W$dzյ%vav"$ R%ج iI8l  kZHiDvH!sp2m5\붶3]>[A 1 8޲-w7S,n8h Œh |l[WR{sqӭ1${h"K&D8 UV;y:A8Fq 5m67wo%I $ u1Vֿo6X,0HJ]*kja? nwcn}eơ&%X"F#PO@2j4ЯE@;z 擸Ln=})bOk5o%Q.t`E4[]T;Hn|>T/&3|Vz)t) "IYaz`Eh{{]Ҧ?W8q%Xd2}MQLNzkzNҬg W;JOrQE#KKIdcidמאMr#ī(SZ9Ky e%B=A5ǤxEy 7̄;CjMv\R} ֯oMƝkp%#\zݟUK)$sp;#h<A > hRu(@sGM`4ۛϲCsR̫ u `j+i[6nEޫԮWǶj{ PM=删o11Tt .REm#,hemxk -.'H"{@SdXo{6DHKi$}ߘi|)a-W73!Am L<,wE2 ,3w51o*-#V9n8gSnGY=ȲFm]qMGCP{uy@ vバ^>q$lF+xXd:ZdHdF]FftXb7%]IpsOL⤇K;MZ$dK#FpʨI˧Oim2ӧ4aso4ΰ0Y[ɽI|w~ïig+DN\ݑF*yq5\Dix!fP G jqAkuAq5nVEs2ٯ ?Ə_A?[T̪-sΡ0(5<B5,ܒYI'4(Jm))sIEQE4Q@Q@Q@.i(%PEPEPEPZZ(?K_ECܵkP%BTdQ@Vm4卮de[j*F3gPOJYP4Ĭg A9$5ۘHy.pX@e$Y M+*OI.{4%rKum6F]A:iwIsX] =4Xfh,ɱ%pznbg4FP1@"Z^kW{K1,D`p0G5;d6],1K}X[\Gkkm,XAUr؃4p- Krw_W7DAV׍1޹#NnBq 6ڔyC694CNYs9$ݵ8P'zIumpꬷ^M*6$U;]RӖkY-dX%,vA ;S ]vmb)\M$h0a4[+;fV8y3gG_.y@66ê}{-BmlhR)cJ$TCIyozh7ĒBrsӵ %mBK;H"FB'暲[i<ȼ@˜g*{̵5M?Zg$hVp9!֬i:eգ"Ad1_q v0._:v c߅jxRZ_wP$NKw5OR /.l$Kҋ ˎxqt!$X`NvVTX$Z;\d%˘c$cPHxPpx;meyo5 c9&04ڢ 3(sך}oq[ʟi{Ed&*Ưᱞo͜6Q[K}] =y>{ # ~d%~e'8!j$m?h}a+6O\ơ Y6egmR9u@Ό%;>M<;ns&r/c`F9'igo=gv.*u8ąbwD{Auo1d:GkYdm xdXc99V#p֭k1F.^\OsBш 33YAk %%p3W5?R0%7Fq SᨥKq]5FfUQ0?RРn&^KEPI:10Iඒ( ۀSsg_ټf9X#kU/.;h$C<~[+n0 ijuin&_aAn`vkqkEwVBVpN ;x%͡&2um D[ftjmwS4q!c9a1I%-.g W 0iB(%l$ m)X3FҴbA:n"ʎp9:TcÖdi&h#<jhtLepyyh`)wR.Jud0 rInh:lona-4j288VB/ K&z;iijvf`LAlP0rIm][U jZ.&{hmHЂ@2:wbռhRK˩m#/QsWOI:th<)8%@h6O5-yK[VGC\jrX&ѣ0AH3PEUrq5\°޴u +'V*C!+)N]^':S5@%wT?:t-=NTȨP+maR<#JaKߴø-kc*Fcک$MB,n-m-$lسs H-h[Āio_jYYaʀK^3֢Bƹi25 Mnޫ2,nR%mp#}zQQQ "I%mfgw;ww=M!,H[ !ȤQM\ѵص4DWi>\*z|xY6^b+md9Ga6't66Y%y rv@-KQE!Q@Q@^(kh--růyA\ (?!RՒQE^[aoe$Q=ǖ^HIZuO$RKvOc?|6א[\OEm21,F}cq_6J.m V :Ewm4po<$4&Eb:94+m*,#@p\83t;WW[,QKco,, ;kJ+v `ɣWWɹwpr0;wS&G-gAP: |]^Kjq6.n-RI~Aӭb>{cMEZzt:೴[f khia tȜMVrly]tc9 C⋨laHKA#bS5u;127#]q|i[I5F+fmt"&|/ AZqy}.}[4i73 /#wg[kZ6RܵA3$q < 5wXMV$}9w)FkpYE{L: YKmңv y09/'޴l5KOUEZCpDygHA>'$}3ڭ=D+Fa1zA6-[^HxpyvZH>Ҭ\i:u\Y,ˌ;x㷆e8¼B?B-sn:].17uRޤY \81rrn >H$Lg\R&c|?O@^';ג1"-\<[>ё珥>\lu;< yֵAl`%낤in"fHJ ty,jV FP ܽ8fVY@ GBOhQEQEQE⎖?l0X9 a__j>KxY]w<6Q5-r)-ݦG'ӻkҴ4ZmN߻DcH@$~Fyǽ(\ڒnxC!BK3ya--歪!9J]`.FnNf;?M<;Ev d3[Vݤ͕݂dm*r1qW&Ӵyl0˩:2!pB2=+J2 U'qpX(KB ?LF5ƹydOcUWeqj/gmQtHQڮ9lӦ+um6K|ֈn7䏘t83Bb_ Գn2>ci f>L (cK)2ɁI8柧Z5(l ,.H9.o Qϖ2F<К&S !FrpJ`_4QHdF!X0' nvfk`vT>Zܜw6? LIpvH0\r?:r\q)[QDh3tX 9nВ0gns7ڭ/SS%ojMt5vr1r+88mnVQ-֑Gp$xWhɭ[_أV(~OOݛ#I'Li#mVn’[o%q?=)j@^u3`>ȱkQrkb"]y8uUr5*b%&7* cO|T7C=E*PXn%!Q@^Džroj.%Mhx_U'cWC-G*if u╀ GUONgk&]#.)~42] aQ 9;ױqYP[xc2 Xt$'}IL%烀^jiۤ3]$d'@KzXn 6`б+&T~F5q2YbbȾhIAyM"m<ֆШO$HvpDpBElDicH3BxSNDm̂6ʃ8cM&[$ dԭx}&KSLϭ.jڱ{khʬLT{b`?ZU5HMAխEdZeϙ3Ir Ҁ2|QG:ZZ*-_/-RN!bIBQ.I@.a&O}=j%Ky|@heƧ ATMwj0  ch8>gygfV6\QN}IEFn->y!6tx T >lC *4hpڐM F]FYw(UKVKG"6 OsS`ic+y (y:?7n٧3$,h;TӰ'CԷu@gl{o6Q̮gBFϵs̀p=3Jyr4iX9O&բ_-䔪?wP;I`0:P-twRB$q`dO眿'G'W-bA$Rda|CJyr4iX9O&NsF giXhұsM^>K'SgiX9O&QAH+іn4ڜWZ(#sQ.tt]؃ɤnQE(QF}q ?5 $U`~t}(<?Ro_'<#/f? >7|/P}(<?Ro_AȆ1gf7c|nTxȊGK_E(kh{%-&C>{"O/++2 lU!YUvG8pA⵭?T٫DDV,W54cc$g$u幫[|:e Trcuy4ʓw*NtD x8c4KH_N6aQHC` yqDZv7c>=6q0 $ m1v `p{s΋A +#l%uP>I0O']hrho(Ҿa쎶4j'0o΋C'wA,nAyna\P_[iz1v'2Fd-{Ջ߷6e3q< u߆۵8kWϽko">}!b_3iUR]AI83(}*>}!F(6H z+&z WP[\ apZf 5ϵo">}426YmgRL*H=y¡yo]~o#>yokN}u-f RAQns]?Uu\;Ͻ{oE2QsuU$$n ۭ%)IHaEPWq\Ui2/VJDFCPr bK_.)~@H$?o!H[xfC@8.;ՋX\4pr ѤG*Yt{Iӹ$J9d{v?#*mVJ`HlPhՄW1^n73YXXnC`')ڗKAqgV>X\30(~ISC2;1敼Aq)sc JgNp:jmj'2-߽ l8rK a_+Hd$[)s@)+omu/Z5V[,3F70::Oy*MgwFic1Ka.߮ijijiMCwq-q / -.텎J ykq,5 wgj5S-?FTU-N?տֵMJNN$wN9aO> eF3@S-?P4eiV ZwC|ϔ?Z*eiGV>P5j4=NE9v(TP:0(0QG:ZZ*-O/J}WQ ⍗csʒcUO/-Y&m· Z{H1]H5|;f<"Y?0ϗ=>}R} w&f)2{(==MT0iG\\Jcȥb 0ps[P8ͱx潼XU(QwZR^Gyui?۲뜀C)IQ@h '3u4QHaEPEPEPEPEPEP:ZZ(GK_ECܥnRc \cGf-m#~Is뀦Ѷm?4wnolEqCֱ)(XhS~Ix}Z[^ie+gDG#o`|1$~/v#ZHL85c!{)nƚٖ2 vIJSrٞ"NTuO>Aqjڂii=疂KOQT-cGky jmPqַ{\-#Z#|%A>b`Yn촻bbXG1y^d}.o&yG{DbF/TR/_UY=Ĥ̊JZn]VҤvqN0 <yq5E4 {Q7ˏy(k|;9q5E\@棂+X"X%9'yq5E\@i\G_Ph7ˏy(k|v(7ˏy(k|v(7ˏy(k|v(7ˏy(k|v(7ˏy(k|v(7ˏy(k|v(7ˏy(k|v(7ˏy(k|v(7ˏy(k|v(7ˏy*9#Z{&j~}{APQNI8I c1@SaA5P{[b,UoXh?J>a=((VU6 tRx!KRpw rͯyA\ (?!RՒQEQEQEQEQEQEQEQEQEQEQEQEQEQE(-{Qi g8Y$axUUFI% -v:ޝHcI$ۻg!Wk^iaeuo-.ZT(݂rvVE_KȑLӑ?}zĎ&2,5:]kyt`ZpyU{{Yll巿@Ib.~4t&ɨGIiHl}"=;I.{6-(Õ80 ZltDCphRg)_YT E#?? Y[?!R>?ަ F\s {QWEc}(}+`>m>QG0XvJ6Jਣ,wOOpTQ;ݧҍҸ*( F\sii 9{QWEc}(}+`>m>QG0XvJ6Jਣ,wOOpTQ;JvtK_:2Ԥ>EcE'T}(C%ȖF"ȸqQp^)nT S 408 636 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((e," }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?^c%UU#TXMOjp7/."$b;Tu3qo1G,gxi.}P?:.4{Ȍ鱕U"a!^Mx[F""xT6xU|CW {%`kUe]!`F}${` (3/+uRn o%Y9w:$ŷMger4Jq\#觥 g_Qz/+1ף='K6f~L>Ҵ<{mwZN!+4OhXџL M}عЌ:x->R}To| Ǘ3fOꗷ_ u g2"\>1;InZ.>Sx[ DE,G! ?1^-Hڕ蔲FUwzPk-_M2,vp~QqXv_Ro }Wž+Ι{, dedE#8z|/4Kgį#;}(XB0z/*=h 运 Z`\,^B0z/*=h 运 Z`\,^B0z/*=h 运 Z`\,^B0z/*=h 运 Z`\,^B0z/*=h 运 Zcž6d22r:w:6M "rtDXO4<!>ǵK?s[=9/$K8f1q;{()/㷍o1 J A}0+ڟ_ʍcG7f}>Cv$_OҺݩڟ_ʎPk&mu".iv4'y_Oi$[3q]>TmO/G(\\k^\Kehђ'p9ZږjWZO*(Ef^BJ붧jq*,<:RR/m幙%<|@95yhj7Zev_]^TmO/G(\m`[S5ߩ6%8 q]g"M$'ɳ&rNq^rԵRh5;"uR8,#p.g*٤PfE4Q@hP3EfE4Q@hP3EfE4Q@hP3EfE4Q@hP3EfE4Q@hP3EfE4Q@hP3EfE4Q@hP3ERQEaeޥKt I|eM4V'>N=A\Vο \5<CQ^k*dYc湛?:O6Z[M&rq-,=UvѭIΏOuh2 97\ףSs^/nR;0ˆT3=zN]WՎzTҌJ w+]Cevp: aU?e+M.]FﻓU^EX}6_?”i(YgV!`JH 0QGր"DtU?Jw)Huq*r( O/z?3Oy¦6 C_ <k?R H(k?G*]*Fs=h5?Q_ oMy5?SUI8àt!*r(?5?Q_ 'u*F=k?G)no\XJu*sOy¦}((Gn(5?Q_ Ku'27I18v45?Q_ gJ]#8|k?G*m8M r=EG_ <k?RQOy¥q]Ҁ!Sj|2G@H3ހ#Sj|JBuk?G*JJg(S>g(S?4P<k?G)P<k?G*Jh3$M0?o(S?+y`i=JD)QBӥs1&x8nįx^ "K?R@+ {xtmJr[t=]W,HhQx¹/ xMokE c| efx (]Z^⻳~z_&Q ZNPDEDž?`ԒVU6AǶ=+//̆(Ix>? u};ļ5-ʧw;kbӟWo:#N8o%ѿz˩vZv9g?A Kx:ZDmeX*UO -jQ0N+W^C<7V$ר ' ]\ekUh ikVek)U/Ys8mUkTc%Ƙ!A!Y 6ͽ1}h-JTQ FwYb:e*_5Gu֖[6[;HmHlX)8w>ԗUѡ1\YⅡR= \xZ:1h2Zڵ48ecNhP/T0}儾[\UڹLֶ6/1~&۷>5;Us],ʥ% r sEtXr_ gẍT\X$\M4;IMR8um&ьO30"7@AWtkH'Q6KFxq*:党[[إ]GP .F;MNos&,w.-5^KK*O&֭߃:Ӗ33"( _lW纶O,ƝoťJŒ@mFv&< kjaKhܩmC9īM0M׎%0˩t*# {qWt<_[XϨIiq#!!s]bN68`bq~05Z+9y>bp Qkk?/k1CiQs+5/hE5MmdhҊ"ŸH;vpqOZuoCt-adXϘ` #RjDג[Ien"g.`t@xe֭XnI&W~b#j[|Pj ꖉZq} C:sSGipcĩ9;)ʁЌtĺƛ3A=3[Inc;UHK@5ί-l/54i/#V>wmGJU4(^Kh.Z++29=Ek4 Pc}E ;X}𶧬^ZԤְs CP3i{F|CokC $"Ec #r|MjEo5,2T$g[(-M{"1H.d 8$mǐNH:^s=υ4mC8bF͝Qy@Cu(Em,6S.](PvN;#5y-v7Kr[i,d܉QEooZΥ8d]r0;zj+k-m fVƱlGnv^W ciCxј=j訮G>!յ]ePfaD]׎om[[>^[۫Y 1= ]qm:Ie >+ Lm#J—׶=ͥl~mbpfa$|O@1V[KRuQjvb)}V+Ey, ԡ6#',6ϥuǭ%s>0u]"K&h[7G3m yTӼQ{u, 2Pj4!l1;Jv:PRJQ@x_m /6fÏd_ŗ]ͫ'--5(.oߺOeWj8J/\Z%׈eow-'>Z^ [m*iӥyx[,˴0<`nM"V.8gQ.'hһQM$80amR8XIkG(V2 #^oÖhgiNR_Ƿ6J􁤝4ڷ3\^ğtru8LVڅ0NVX\2\ڶc%*6KD| K}RJQ<ѫ~bk&c==l3r ,f2Wax3־\25RA=k_x;BѮ|=v\\r$ܡ2zKK|/_Elq fwrF[qIN~ucV4Hd u$FFc '?)䟭|~G"?R/_[}Zf_X]t;[١()J F|'wy\yqϛ 8.z_0o0?Op41}fZt[6,WwҒ_w\yrż/W!irx5x&X6uCq$Lyfi| YMKKo 8p@H?  g y Q3QGŢ85YhMg;ܻcbǒI:LڌחbIH\\<0.GW'oqƯ&|j}^,ל?M'}O[Mӑ^$r4ta9*?/Jkmx>0}~n"I#QG-~P LJ*Q7cAkDng, 7q5q*y5>Sy' %NⲾ0k|$< ${+6F#_~3?bkJXIՏ4lcW R[/"$;#Qh 4-!%ᧉȯ_>2?jA?O_3τvHoYLbFc3TzW-4k$b,wr8EVUxp1$hx 'GWB.G[ﴘ//x/;qK i~ӖDϝ\Fz Y~.4M/,I??&o<%闗6ip ӱXs8L=*햍ggaڬ[YcEx# x',I??&W!$h?_?)vg#A)0lvaem ;Zg:XI<,>йfnOʪ>W>.4M! 'GWA]>Mr=VY/%O6(v77,y8RAY[@&nn Ԭ!`]A$hi 'GWC>-.;YnI1;T^4op70c|aȯ,M-_Dzn Q '@9ϋޡ߇ EMsT*s{F9%m=Z i<۷ I~4A} ij8.4L9P~bA W DZ///G5U?X+OokZK.ZXf<{J[hZ7o"IMǹi_9cOO,cbo7⦡~k!PB!$r=j'BqܨՌ=9 K7@ Vo ikvZn, ArĈvv0vG| S"ҵ-;ͼGdR>^5jX|#ܨhМ}1Y5cDt_l4R]B'1 n^bX1U~Q84^f1)bFTH?S񮳦G +nUUvݥ_"<{tz%KĨYYr  pҠD[|y팅یzcz;Ū:dvwқ_:oy6l3Q4ԕl2&8ѷ*!ʠ<MH?O}E??&A}?RjTFi Xc8Lq\soVp"L4c\EFN)_A??&Oxhɯ~?]ٞxOOԵ}^iFx.rFqzJjS^\dX;썤;ǞGyxǮR|HO<꿯-y<|޴My|Qx^ d!A ƹ+Е#zUUEt6E4%۫WFpחx5 xa03:(^vcNU U=)USTfi/>q&.Hzƥt}@iBƧ~dW]:ߑ}24FU%?ן+Oevչ=չ4V<3q}8~kѼKcD]w,Rz3IcLˊ q5F$"12oEQ04S]JQTԭDW~֠ tC㭑;Fb㚩 Migˎ޵:#rWE)iH)L8Ts8$jWSa(e5u*\m9*'= |p˃Pvvddr5JĕT}kݎer@^$|K/Cm^!|K/Cj,wS#M^WH?`F"ŭ'#!U1\'UFƧ"/Z^G/]\=/Z>M/=)s$bȣjZmo|wci{oŻ)“T"u$r zSe}.OHt)xsgϹ]#s +2IUPFLծB7[vT9 8|Ci,1Z$ 7㫈vRA{Մ{W^ΏJ<aZ@eU>DB6@؊X$"-K8ɯ|+ dFAbWqFXջ xz?gдvELRE隇G$cP">+Qʹ1H#a]Ҵ,8̨X=iZcpuϪZgJGN[xzT;Jt=z«K$E^Bo!Oar:=oExoQր{CӉ"ǁǩes#YdSKм%ڵŞk#ISг΁epz&o8 "k pG4!sx]\1_7d)aqB̬=sSSĩ5W"F<9@ 3 _1QA>x'H+OF|9@3 ?t(ڏsXV#(5^~fCm<'\QMg1nܠ--$<j8iwbSZK*E8qy)<ߴ֬E8iVErF,xNoI?W C]`8Wk׏4= 'R)+Sh8"t_'zwm=KG׻^Z}e=xn=A״{ ݘQ8 =ֺƝX:U7 )* te2$=aXlom2 tDa0a>nEogV ۗ>Njlv柢 SϰTi[cE^|+Rr+r[c{YM:QiKA Nf}Kux%6`8}O%VS s|Eo",P|U?*<כ~!ѫ BZ![\qX/͢Ioԣ .$gi r#$ZT(91 V#=zC:׊kڿ&GܥtF:U_4_ #QNROnHR85w8-N}Im™Gp}ы:-;4f<6JݓA44|vH~Vhօ;TL>2'ަךz/X\BE%jg ΋ukftؓO7=(R-s(P8M<$Zhoi Y?8b7gU4uźtFUBj<|__Oe-rsym8G>Q:E`[. 1 I$w__H_ڳ\4 1f%v3 ˰ukOVZ 1<6pGz5)Z##\IUl\9=Lja5{Yo$PbQ!\~\m1^ iy:}^[U,nbF}Zvҭu;T:j3²۲ˌ`Z\+'[m_]_[< $O!@znu+J$\M>s72ʾbR@\rEu^0{KOhګi[ʦWtQH5Ɖ)J4P`8MDezK0ŽbiẎy-yGL+pyZ_[Ks=F3ik%rc)8y^ t7tbUxѲR!8r{ėw:2\1K0oXei,```s^И5qZ3GZJ.h%4PQ@ Y>72MaY^5H|K +$vԎE5=>,T<^ m;F'ڼWÚ#\Hkؔ,# F=FhhLc#HJ4m4<\:㓏JBNle7^;WŽc3Eצ#O<*eNy˥j!!ED*~{c?_~_z|Q}QKd{*˹X05?zKa]z54M{%j,1?[cRz J^T=F IO~vaNý;{c rWc<| 5gKRh-\,8Ցj/׶^(m& *?!>Ō8׆g%nfkgPJbTm9b=kn6Pno G@z]?ĽR-pi-Cr8rZPQGF H=k{lkT]:kBFx`Fvl.>@rQgǷ"B>ްVV[7=JB|W>%?ufy͔=5tQ5{XX_>kDgHΡpۜ|́ד{3Pv|q}$I;=Q2$]$F68>z11Rw}|kJWl(D]NbNlE7cV3I刀GlCwwpA$_M q| I9W$YW?EJ!{,xz;iͅNLkrNuKO\F!`s1r=OwԦ{+J\ϭV_yZ(V40Ay#"I4_-3¿.~u7RI9^! "HX }WX7-k.[| {|J/-n$uWYkW#W,V)IrɜFNV*[w%As@A pKD3{_x$5VƖwu&O,}3ZŝKd 9{yَكbe+N9Qxr}e96s..mj, knS{dwMݞgz~6+{9bSjHeǗ8j/uCo~m7~ﻻc>s~ڼ,tN]ֿ̥g!A)EEI!=$8ڴ2 {l* X7R:)>@}%t?Zռ oD mlȦv0j:*VK|rXi/ӯHlK dbá `k:~.-}N;DXE`6cIؚIh*']<˻gҒPw$_QX"iy72 }2IҬ^kjv:$ (btvg_օ_?4vZ7i sA=)vU=Tg4&fwkh- r}HQ@Q@Q@Q@ +mzяX8{,ϛJx5ŭ*n&HğAZT1s^ϩjaSkWMe"-w}+$.ePڔv~: TG~h jOy0ǥv0kM Xy鯤%n\vIEAEP1Yz*ad[YOy6KQ/m:V"ŵ/ͤI;K4-*m,3Dcp+t좮ˈ1Wl|M}[h6嶣!USa"%9#3ִ,5۫ ȴo* vۿ8k^t]2mKR-`!;I?sޠѴ&c弓HxaOdsoלrui?½{rc?kzψ"Hn836NE Ä#tC^^́_L8.C|$ DXs\rM#oO.qާfUXU.OQՇ vSU3E.A(ݻfr̠e VྞFs\b G#ӭ|ц)PHI*UZv΢L>ܱy l nۜt9"ѕ+?f7MĨHr5~7tFYgJnVMX2f8TVvguop27 )ֺG}-/mAT #?xi>¨k:-ƅ{mka-q7v+Q i@&}P ;DoGmhxnK,αui)˭#MeybL@ Os?DXDiH"8PJ&.'2y2a *Zo^çcr|99m9ӽzCe݂, u05}uC >r 9 hs9#U8 #^xtSyPPW ӜSK[yWW;K{<9X2^/m-|\\k7hZݝ7lvUA݅2+nLj5[XLw~yv@#%X}Xgғ6Skpr$048e9>GZ?Rx/y㺖Cbw#i SgV K9dqu\}-W+>V}&8Z]·wWZEro`b|30>]l.m.znC ( ( ( (VG&H+p o@=@#q]*N(0MZ.gnbti<@ǽ>Rsr mvi(Ś#?PqO^_K%oCzxr0RJ( h/md9aC+BZn-7V^6-yLzx3a޽ x9-qy<*^KR/O~#֧.'B֦}{kKbi1EwcGy\js6P,um N,aY>׉t#i!*XuOxШn DcBGI, ǵd_{k%Fǀ|!v,҃q!wJǒB]}g*PBEYXJ3IE4f\њJ(sFi(4f\ihuW\:aMa13@ a1*)٣4 &)٣4&(13@ EɏO&?@0l?ͲF*ӶF fO ֒wP8X))5ZIk;U=⋙bOEń 99:4kl`#&tuT+s[R^A d;Y9"kKk멯U!!p$@ilE+͔ͥidP 0cw峟4А 1) )>VЈ"y^WK yAwLVe-m巆0dTmF?*i1X׉?HH Q6H'ViښG<6VB%gFe3ހ:+oYT=AM>"hn`9*[Xݘ' k< RݟUz:gX.oj1xGf\bAGԑRa强M<Tyqrxb?8.SEsxMh.dՠ%UsvNrx\XDPH[_yKe,nh\ErڗIӵ#$v +i@>Ti@ X<_FY7IC KbI HѤ~R!LiF?Uj|H:iocݘQY i0WǃG@Icxf7L,6$7e5^Wo k~', ]T8=S6ʈJZJEQ@R{}InUS8.Ǡ}x׌uya4+/mRc79ˌ:5/3Z4yl=3ºY;iG A=O~Aි!J%Y}KG.5i,"\|Jձ*|w⎰ևjj^1EPEPEPEPEPEPEPEPEPEPEPJ))E#\̟5iѿ]3\̟5iѿI֒R=~s w$H=U5 C{aek%6yA)W WC,B88#D*D~b8t|7gEwq7٘4evb!L~9j7vmaBG$DYcٹ[8<]?$n<+۬cenԜ/-]#SԴRWG M޹|lWOa4/-<ʼA7nmm$E$ FOS}8,bj:hl.bG`@k?ĞMbQO KqƦʍáwJYJuRj8h@%A9[? KinDwjagk,aP1'mq6HIdKO5Ԩld85iv/,s^[Ң$s!~xiEƙwACHpF}q]e彛̢ۭ7E=]aRjWu^IA| Qr)V}ADlY0.2C@TlcOMK)Ĥhu$dzREY^"WT#S3Ts.mm$%?TdTƀ=}kW^u?5 4?eOkn5Ǫ}+^w7m%?&E)(LSY`.|n^ƍnB#HMS༵$ kgşy<6Ks9h?ku8;J;S~t5-K&Q9}SxDzjsm;*2C?=Gs^}g_OZ+ic2x޽'<'+F8{?WҖWu'Ǯj+?dvwoȓGFrq֍sޟ:໸.\W |z wnkmQHaEPEPE-%QEQEQEQEQEQER@ J(P?5#V]A5?5#V]A4Ѿi;ҷZJC9t!7LڥRCżVUj"m2NJs^꯷z+m;Fp}EVhOgP4tzX#>}9/=f pՈf#g",a^(5# SZZDAwmhKrb\/ӎ*C&eFZX>j7ׂi8na{`oHmػ;*wo{uhzlbr9פG;c" 0a}hNjj"Rɽ-{݋$\"w4[R -c*L{G^a2$o tW(2>"nm`1^h_W-֥q{i2/} &\8T/t[ eVAs$Q#+8C`xESCZT']-NX4Xn7fa,2Zq)ъ<{s0Mq|#ҬoM^Ū7lWl(Ө9#`W{^WAlYU[y#HHyym 8tB5Y%G%wה#F+#9\-q5>'$1;yeRHld9E˄Fod@$P+3&|95Y[,EnMLt Nu4m~ xR[~T}W S%{V7,v z(םZdojj!/B+.V98M8u V NA%q+l?0%P&xVc2Gw 2{\ ~Km"H5yoa{,e"(b>cRq@]mW\wV~_&7*AYd:뺏t[k0\HYUCNNܜNq@|c!a$]iA,O%!hgH{?|r@2ԥ5uX&&U."r c=]5?ӿ&Ul^&s Aiqkb~$Ӥ 36ɯ>Oh~)GM1VR021zO jqj"Y2 BMA Ap\X_ O#sm&9pCp?K;|YYαM`naM.ҿZ-Vy&_ckEƩ:Ew; n]G}*Z]B0X]EpN߯Bk~ ylp[,Wo#n*,Ιc=ןrH"˵#r~u'P^k(}|j'qKPm. ~c+44i^n>tLŚYV);J?wY+gcoi0r}k?4/Wf2-F4g|j"(6>cϵuM"EHIu?d@b}1n]yk<6CWt4Vi=VnvT<\ VHg&yd"kyzGu t"AWǡ_E?x!m޿c;psބU&ZckVVwm58o$F R^xq-ΉXZM:f!Hz fHkL+I6G>`7IM$[]M*_C"HcܫmR'z}P,lL8: slb \Z[Hw}vLz1`-B"`R91%~M\[<)T:J~j;h#c@b\(aO TZ?>xƶLS[KVo c Xm NWizu|׶q&QQ?U˒Nq__Nu=ŃMu3jp#mܲBMz%%)(( ?kn3ُ-~Uq-+y55Po9t;mwg<~##8&HVl˸8kMnoj<vE']GoϯZwb(jwgmỈ|[w.6ۘj)u.OkԺ5fhnx|3 '8qθ<#ӌgHD>f;|gz4-ݦ\jMv׫HYA'axT՗[}m& ƫRAv׏JºkZ;,lV5-iz*9ezV%4K>,3OŌ^p&f$y8Ys84溽PJIUU5e 3 0Ai+"H\<`{ 9(gJE՝*^ۼIle 76ᴃw8|.$W-p>z9XqF=sKۼMMp.Zy/Pgi@8. Ƈ1ؼ/K:-[\ kubt~ƹkyKia-dXdu\kz/1\j :]?e # ںO if'i.$@<ۘ8|'uijgKˉmc uڽqh߄5Ht( [엫t3`=+l ͧ\]ݝbk빢{(0@M`^meu o6$Ȣ@cP=~Q).ᲒM>nWbiD`[5C㧸t&uH<R 쯌7,zb?isk:֟> ~5'Ki1EMoqkh-\&ɷ ׯ6\C$6NSKonf@ <꬟'q('=ǃYZ JI& ^MGlA}pjeJU${Tzg+io.nmK[xG|oL3r}}hVmvHS\u5x+wiӋw 1r=;Jv7SZQdö9'r3e+ɩu=ȸX@ Œހ2KM8-MՐ7 ,||.sMU/!+y[ ܊|:6R[Ϫ]vPmSY8 |r''ҡtd.'Bo/hpTQR`źSMHF]8)20!2?;K#4WEKuel$? 䠽,ctՖ }f2F sе?ׯ{nke&l.[|9RJ}2AP[iWRj7:qtv\]m=i6WiZweP%ؔ?eTqT|YjyԴoj^0E)w;H8\[_LBg 2I8_Zj1vQ>P|;wPȰxf  $v6-&W ȤWBXss ^kj1\ ʗZæ|22W I^j\}o|mX:du _ǥ`hq:W5}V[}XV{."i9O \]x CO:길r[LrхÓw.z5Z]) 8-yb|(NZŤ\7U;tni>%?ٲꗢ.tYe+g\{տ_ж7"d@+5&i(cIIE.h%4Q@ +;+_RYlF0޹9hU-AU|r~5qio+p$̎#!]q[V΅C.tGM9" [ɝ+ /gkgK]+cfNkg^O$ofU_.F:lqT]Gȴk4@ ,FKw ri߅ kMǕ';sc4)i((]As,H+"2\d ʑmFop9]8HF^WTAԱNTfJx4kŷ*$@hntefnZŃI y3VƟy퍏qID=ԫ]+1{8VU+iZu֟kopM9"j?hkxE|Ly֔SAUr ^Α~oT^t0k5eao6pϾ1]VcNImvMbkm8yi>qWONg ^/;Sگw78QbX,L*'g=v~ȍiTVFI5Ru tK#uz^G9bPH(A[;y xY&5jRqNŻvtFӭmQ3qW&Bleix]q$zWz B\ݷk:GFIlp8'k.mQ4c!Akw-hjL1!x;EkG2;"C9O^_Mcø(#bl[#{xI%sHzZ6tf_CFNqU 3&ۍIƸo la*Wo-naD GL{炕IK]}[֡rg߉|yaiskiܘg8Myv=-*Ǘɞ=g9ANTǗ'w52}-7{Iަӟm#A6ii%\su#(/IqTl]o&nl'Ks/#B#3@= ;Kq1`"Q7P08>(-1Cjz  aיp$cKwiDT2*{w+d`kh *w {[[g9v0#'m h26=IkDIEXmC:ާu{_ K٪۬k"wu_Jъ+V đ=r1ʋt,a| `0@/eXMmr~d2,.RU2r0=$ӮtoXɒ4s*LdPZڵ5 r L`u6筴?V_ןc>/;:O/ǿƝ >vr@Rd__jֶͥ>ɻ#}knZgub_jY6a4 (), \WRs"=S[iwWN+RRle/$R)( `ڳdEa?{N+tk}8͘r$O=;A`*Q|+><:'@5|r"~7:&\?(g[ZlC>c< dQP`1V 2̀{ڜz.{6@ѩRT^sEq1!6|cFO&Elff p3YfBq/5kQy=[gcxzS t>j"mjkzI&B#]_|kuz1+]ۢ' HDV65yfFj+S$bDxdNSX{Xfka #j:]#.d8xn$EmBH9߻%]JXVӥR.)Luf;;( [=\F2O\vHH/%.Pu`~&Ue.̟6#dk)lj,`A>c\e4w nTw-ž2*BĂ o $ DjY .nbNO']QӒ:zcXs>c yrJڸ]J iN] MK6ڜF0#,1R|?mX`tQq\kŨdkg+9gBvS_j6?V٠p# AZ3^_7MF't?K g1^q*ɜ:.r;:v ;)쮅ң)mNsѺҺڔhi:]eR־ Pϔ@0'Mb+#&!VeTk'9Ak^y^I[D{Kg2y)n38ټ/7+ ̮ ہ R _@9K ]KK-e2:u;N>[Q_sZƥm#$FF=@ъ_A_y\FFI-E]F!M$E%` XG<:?n4.MK?&{[^^ qHri,EI 1DFbyqOu*C s8)Mq7A,NH 8)wuqL졎2e9RE$sB$N0=`%Q@Q@Q@Q@ +'#kҵcx~u[F?ʀg>x;XOw`}Ѓ53v7ݹPqZ]#͝8\e'ZF>]I፤sqGj<)YTxcu+K=ΗSds0hd,G*1z ٥k;)hm5.ZElۊS$w,!*(]m$f츽  CRJq!x=X~^5y|r0[1֨59h|Ƒv4Mg9U::Rv<-%v-V($H-9^zJ[ۮHnpp9Ii/k]b8"۷Mu60z}2,J#\\E;'ȻZ\`=-] [8WCk^ #󝢊F@Icג ¶yE&*Ŭy15+9H5?h+yno1#6' ۵_e` 33{ȯ|"f/Dg-;ky~otWY>R0=MgԡS+cVʌ+(5 2pr $ۢޔit$xfc+sH9Vvɝ# n?/øӥXve[2i$ r8?rq);-|i1W̘pc[Y(N܁,Qbgp+XiZc};7lG5Z 7[$LI90ٝ8ÒvɶvAgI[͉*Ju-jyot|< ޴Ѵ"Ļ\ȹ`v=Uv=E[Vq6XWS$oBjbn/\Wlga$ V}|UbFCg<W=ݤ1RXq1tNM Xh\gI5RV܉UG 'O/%# N'/~-i}r8Psj.kȧfp"Yiw; ن@#'kONR߯A*pg?ަ?48Gj t&?RM'9B$Du$#A*'-̽1~|Q帑7m:z][ww Sz9T24-?OpMjYmQH\'9ۊζ {8,T}Hbun\FB]o,m&-(BO=y=}Mvz(:g}@./HC,XqVd>![.-L ?u@9SԜ뵝. R--"KNNl-97gjKTK:m[v`n䚓òMk/_nK}cFxrMwQZtW{hIis-Fķ#){qXrURሓ,F WGU42 ss֭ДQEQEQEQE(/?|DXw3o(|c oc wnɵza( ƍ1NoZڡ;<*ns]Op6q74BdǸ zƭsq"/r-T H5ޫ ?/[oF s~鯯&gg4>b#]p0+kl\4QESQȻԤwG$dZ7}:tB>IQ[Dlڔ6w,Kap^NkIF-I|Uytڔ$w IּK53xI-@Ҽ*XW b.ǃ]~F%8lPk^ 4d?l3BH^.M#acmXI8ȩmծ mcqKs._36DG5lŦ"右'$䓸 k*B2T{s]znE᭣pHV5ӃMUCD]Y?Y4Ma 1e;Xӭ|eV(;ɷc8ҾǍjNOѫ̠Ÿ6Uv\]xS素^TdyxRj1tTSj"hڹ0sz~U1j />Vr+3xSD"&kEX07ƟyqCa)!-ܖ7s^I%ĩ$dFRAH..wi LVGգ2E4[e:^"UDtJ#8Ks[h|]eM$0?,xShD ?C@={Ⲭ-Sx.dlh^Z(91S K1[I:!e9p=5m|I`4o[q>>AO3zN ՞V|:Ɍj/5+=>;mBMKW9 .{$h+Pu,K NTqҞ_NVzLՐ, a,h95kJluYY ЀdX^&8p tf g2٤H dE`*Gz~uA5vI2%uz 5:IJAE. @ RbE;`hbm1@+ !@f[7 0=:sw2FП^>g*nmR'YBt<:34pRqazSQ&Ԗ_,r.աiZZݬ更/ m8{BI;g: +  cƞepl8t>?|8띆WI7#ɹnz3'$WS}IS؈RJEQ@,r V*돕П(Ǟ YƇcr3lH8*}j. ^lCW9EUԦ]:N3J.F/ִ۟}Hfk$ug͒S޼%NtKD%Ԋ%a#PY|qP>lr_H.J<ǎIJUH/;T fxu6)>8}+>"yyFu$V.niƞ߅^>]SPY9OAkNJfY!ϐ ߧzҴF[M]fFLU=5x?~ZX>ډݲܖ~{[JҮ.)*9jw$6]TBc[ǿ흾g*ѲmW;{W'"B>ddCd{q2W:߄ z/.̊ҫ0]maԮZ8 R'8Ͻx}^B:7($5^t0\(\sҼ\}60g@#-(1xDT azi_NywU($1$NG=+)2vgfY6rUiYZ@8K_- Ss׏vB`ڗ311qd2 5b]uІ=; Ƽ݂I-`qҽ3yIMwkQkJQP4oÂLdW8P OW¨Հ;TqW&x|tc7vx~*ǃglBOANWdʊƍƲx*~d}ZxƜ L5 mZƍƗiՠy8gBۏs:"#y; 'Skַ7_P݇ՠyRx3_dTrx'Dqem_Y⧍]r5h_tV|JJ?/қ7kdXy; c*Wai|RӍb{fpOUe=f1*;dz4XRӋc>/I!o5 >IH%ukHǸF o)-RkV^3垿0#=_⏌9ceN]/nZ5:,-xvc y94 x<4*3%yPAmvi@ nA*#\ːR[u} TzIk$Xs oRmo-[8KŏCަxvEx"E%O:}5dzLyiOz/ej2R@ǟN01yg^IwjvWyWR4STq6_|kt?U}߳F=r03.|U%kMN)XpOe5uۭiF >?J7Mk ' bii$˹旟>&Jڴ,FAv[?xGHdD#HJo>%s3ZO{<[мqo gZ*,Ĥnt54]xNyg~u % [#+#Q3AcUDkӳҧoňtGqҾ s&-I}>w ;o'? ]Ϲ4{O?&6'?-}WQQY>Q?~'D{a ~x>ϧ}kk1Ȣ⾰)v4h+>iFnvxۏvZM wk;֏=iܟf|Rg j4f]:F;Xɑ֏=h{3'A췑B#vfE_VJrӚV=MFNia %)aEP65ceB<h|_[*$bk"rgij+@=*Ң'Gs܎V:n;Ǻ]N$ ݌Zr̾\FeY>5?|\Jb,5sдv[XEr޼WjoZjGJi$^ͻg2?bwڎgGԧ DMbV(sY}O 4F4yq۲p3ڵtN_]Ag[fl{޴sz¦OFƧcl!Ԛ|iWUۉuq nz5ަoZKؙ; - 6o0/n}q^h-51]Z6\_XyhXW )I ?coW5 Uf-}G淩oSXgElOn]d?Qb9;휏~^Et˥G9pF;c޴kz_b}>GM'VyUR-dϾ+?g:X^InydCvWiZFiC֒]њJ(sFi(4f\њJ(sFi(4PFiPhɦ@ɤɦhiPhɦ@ FiPFiP3IE.h%4PQ@ 3IE.h%4PQ@Q@Q@սΗ$vN.UT0$r3Phv: wa@8Sk6Rjk VrpjKaGo-㻨919c5o}>;{0|ImmE*nmB$ZןOG2dn[FFXMtCgamebQ[-v6PZX R͓k_gZ04f.h%4PQ@ 3IE.h%4PQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEf]}imp+8Q0(AIje95r ?p V=tW?lpnsƵB(0(((((((((((((((((((((((((((((((fotoxx-15.11.1/data/images/mashup2.jpg0000644000175000017500000003464012616075370016124 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:resize|tonemap| 4http://ns.adobe.com/xap/1.0/ 221 280 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Vc5TU9$rj/?Z};GF+MkQw[`L@\XOi%_YRvHn"=eR}Dq48%T`~'xQho$%I]BQ,b 1:s[Q`spU} Xk˂) qTèE~'tyQc$2`Aڳ5=B(.to$2#˂?KXe-viǢB]XzoV;Y[)Tx'epww#n_W]Rм{-FI.4F9{!z )֡#gWKxHZц(X` ^cڷE 3+D3"̜doK F;xu<" c =NxfޥxĚuc,piSot<shX!F运0u[ISSlcciulNdZ^/5r6,$,ڋT=o Ͳ]ݞtK&L=wEǢByy\,]=o {{pwx_Qz/*GE!F运^gg wEǢByy\,]=o {{pwx_Qz/*GEi:ppqUOE a1\,nZX.eV9(zW~!E/J5sz8emZyXԌݾ"?(cQ"0VFrܥf$Ga͹&zx&?(cQ"s]C#=*A6 k1(GrrOHw0r~F d{UKwv/4eTminq+cQ"&?)r"_xf[(-$f ?{U-axNm)o0 /Y}p1tc'xAQ"*/( >h(t7޵ȣʋyG|\s'o*/(Q"(\}}[ʋyG|<ȣ.dyE<ET_?Q2|zookyQ(Gr̟7ޏ7޵ȣʋyG|9BOGZT_?QE<Es!O=s_?QK9dX}[]^I4#\~ծyo&6(Ġ9:qMW-.F{t7i5f5.h[倦Hm5յ @+z('+ɯ|i/k[v'!nыsY>p&x6\zl!BHoE}NExf^1{꺥\ `Bp:Rs] nKtxT􉡝潂0~ъ 'C_zr/'DAÍ5k^4SG4╥ GR懠-OC+h$X1䑂ROy/mei2%OJOxY =3h&hŽd+Z q#c8T0~Fq`t]]C3qgsrI=2hn9],NXz8"/ ]B dT}D-M*/?#ܵ}j$0Hv 붕]\ ҍTx Oc_Gmo#>O),W1?(eQ*HD!VL?dRV_d|-^L.DۣT V'JԦ՝Š(1pA{+8-.mD+Y%W =@VuMobGc:iM-Wr_.tېwRug?罟o*Eo(nu"?s#_.umQ}G{Z߶⨴}Ɵr?^>=?T}P- {_qiZvSC SyHNH\kr_.uwmQ}C{߶⨴QF?Ԧ)c~ucNఆ9@, ֦0 -k&POIhR@ 3ڄVJ)(I'4yQ@ƀ:>Qqv=I9 Q@[ƓoSML5jf[Mqw̶FcPotK/&&yLK4I%sՙSZWQ@ Nzh (Stg.."SIbt%^Lpṃ~`I}6ZՔ\[ZK4e@eBFG~EyDž xf]°DB4jH )bskSP1<28;Xp{pk{W:imV{X3޼H*PAPX06Ͼ{WhwwzUƣh,dPZ!(sֹ|Aj:tXđJBٸ={K]7O8i29z?++L?F)?ʣMU  $f(JQk I=<7u?bI1tF$&Rŷ!n)Src~V%iZNͧ4orݤB]Yru[x+DYX5ls3|{7| ,Sy{pBI sul3욭E207`GBm4~Tnݴ㑞foriQi/aWLBWtȡ#DRBIiakp) (QYwTl}h7@~A? __t}J]SKPN//6e$|7-rkc'^C )qBJ]SzWRo6Ҿ :Z98>WyZz7tm_^n_پƍOdwS\zWcҿT.Bv,%mmبʱsUx+Yv1c*}xtÛ?"E!(:sM(G<85.g{i"e[1p$H}(xM%ܲz$ :dN[۹bFy\*#<OIYagM$yK7Sc>38Vb4a`W ݜb,T\ѿqzn3\u(.(_SO/-hA"G4X~^%I H'N7aXOC޳d.醴KK--F[jz)*jkkc7-Q\#1[}kITZXu-K EdS_6o첬zSڦtcdu |֞zPA<Edxx\kQ@@'i'[zb(.yt[YmGl#C)l+xX|2M;P&XO @qG\}3yZ?O9NjGm~!BMh.dTerNY5 BiMhmP;FYH=Oz|_{'羙IWG>ԧnEofX1O~$g\Ҩ??+Wg(4-ywbjs !YPJFr-@WWRxLӤHh FhzCU{]1bM; rv .8cEggpj.*De#jy޽/4fv?Vu3uCVzs[̓A< c+m 14>SZc/>KGۧz.h!_>OOYn溒&mΒTt2Gyww1!L\ƽc4fK[MRK OD{r5y4 1_7KD٦h.Wd11|nےq1SWMG}QK"Jmp*IN+Fhz_!],>S\H H0xZ^mt.ihKxH]:zha,mLIR5Wu rqۚ)P*p]Ad3L*-tN&˰obpyHZb嚲$_{W>${=>G.y$Hx\,{lhɱ{ƼO(VtJN[ gy 9#+Z-n/>C_|ZDDDxWwɹ6?{`En&a1]z(J}3Qx#P|CuVQc!Gb~n*Y[2v~(<|bP7IAi??k|+?F-{4W3sk̃4t؈ivx"$xe?Ze#KicUֳt28 mpK cA7V0D]Nv]{=W0ݸVp^0t3V65:_EյOcpGϕBr:կxX6SH̴m&!>c%2(]s6CFk/uūm8좴1 ݳZ\%Sk:c[} n94%v]]ҍ{k٩Ig)- [;>Ctf i>{Oi$k-kK[Q7ݛbP"-#W_r۾55Mqd?;t\EsnĨ' 2= X} x*xqk@of|ۨϗř+2}kzjj4~$/'9w)w9r1):ӵXVV*vp} .^el|iIs%X&\,qIۂRu[Oj崗)V6䍜epv#ԶCU-tR[mwXp=ϥy.k{ _7F>X>nP9k&}[Tմ[K.4y}m4&{R) y/_? r)mEK.?KE].? r7 _QwR "DR}W~? r7 'YԱ`"o<VoQ[f]BGpՔm I sy5)ZMDMRA(7 ҇+БM$Ə5j:Z`\WJSRd$hHv$7krP̌mgwi!YW nUQqv5}X$y$RXgЎ(Zh:Lv9 9$I=I$1iPMc曊1@_ƃ+4Q@21#M'4PEPntvAzKҬcKz2~b57ҫUğjP$jǙk%Z5[(|<{@rWz5wAf 䬃8U<h?|?jzܓ]x B1JxBya(b2i"'WPpzƻ`ۡI&i{bApR##' 5%K_ϥg;-#'?kx/E9,{se` a`qN~6& YViW2x[C6M]sğj@ZGeiտ#VW!,p* HAa$Y(S}f&&nqq¸}#?ygc5dҾݩ4E8MJTw&W/?g_ 3T~0m:k^A7-Č1Vff ܺՊ[\fd+{?^ߙҿy~f+\v3E;J69=&NֱMX* t#Ew~@5W/?kÞ$:έ7Bʆ݌ zs[a7 @ⵔ (AR8LU}x4/lȚX(h(%%ZX%R~@O=juIj3tkVt gӮ5uk'mβ=D(XC|ՂL#6@P+ ˨%K`\q槼.e~WŌʭ ʌ{5)k:]FTm6]V/Ԁ48}9i?j2&^,J9\PCIY "+k;J m21P^+4/!m>iI%ʾ`2F3և-M+'¾"|QèREb7HZ5`NEPEPi2cGǥc`VxlaI팛FzP\|G%v7qI*Mrh'տM?sΟ=cH;;)fҪ@|2R}@O=l$MΝ:irʸP7/$տM?F?oǨ zaIiqM\Ȱ\6$!rҙڦ_ -B  Wy'5oO=@hW:KԮxDlŕV0lO6[tRy'V4KKdIZ*BW `"LAOS@QERp`I2))hJ5-CHJmoFm8umrkmmntPOy$R}ħ983Fh s=O.tEmi PyC$ Fϵr߇5Ox66?`lYrʾX$@xׯfq=O_[XK>⻋z6ZHD$g\'#i^SO2ǥ"6) \1`CI^ݚPt$Q_xֵ֟=Oi%鶞Hh& 7N2F+kE\kvaiFkĈjC(?/MsһsFhsC@𵮕 %KBP8fܣ殆晴 (Q@-P3EfE4Q@hPIEQEQEQEQEQEQEQEQEQEQEfotoxx-15.11.1/data/quickstart-pt.html0000644000175000017500000001231712616075370016274 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-15.11.1/data/quickstart-it.html0000644000175000017500000003234612616075370016271 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-15.11.1/data/quickstart-es.html0000644000175000017500000003523412616075370016263 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-15.11.1/data/quickstart-de.html0000644000175000017500000002542112616075370016241 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-15.11.1/data/quickstart-fr.html0000644000175000017500000005470512616075370016267 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-15.11.1/data/patterns/0000755000175000017500000000000012616075370014427 5ustar micomicofotoxx-15.11.1/data/patterns/pattern_117.jpg0000644000175000017500000003165512616075370017210 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-15.11.1/data/patterns/pattern_131.jpg0000644000175000017500000005044412616075370017201 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-15.11.1/data/patterns/geometry 2.jpg0000644000175000017500000003766412616075370017126 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-15.11.1/data/patterns/geometry 3.jpeg0000644000175000017500000005217512616075370017266 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-15.11.1/data/patterns/brick wall.jpg0000644000175000017500000005716112616075370017155 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-15.11.1/data/patterns/linen-fine.jpg0000644000175000017500000001271412616075370017162 0ustar micomicoJFIFHHAdobed@      ^^   s!1AQa"q2B#R3b$r%C4Scs5D'6Tdt& EFVU(eufv7GWgw8HXhx)9IYiy*:JZjzm!1AQa"q2#BRbr3$4CS%cs5DT &6E'dtU7()󄔤euFVfvGWgw8HXhx9IYiy*:JZjz ?~w7+uk.TY M- 15djE1UDek.E FD H6DwiK9oo$Uj)Uuդ3=;icj[-oYҪHQv44-RN. 6$zyE$^#UuF?^+$߯}I{G vT*֯]&)$y'a%Yx4,@cPqU&-owiKY^뒘$Inyt! Jmm,HO0]ZinySJ"9u+CO,'n-}pVy#h(REWuaդ.,I{zEJ h-/0iwؙekX,W d!wWիΖ˨R{{e[8I-H㷄G6m*[`K?&z5_L &*/`TiSlHX.E#1@ت&dPj-oi,(-5o\U $6k/0E{}M=Ҭ+AG8JVx\U]u;B;n㖌r*nB*s զZtfҭnhDHPSZB-i o [$s"[b[})Fi$pɣ kLUv$S-иc{$Z:X2*-I,JYWb$..Q&^餑Ҭ|;b!./0_]6x͍KqA<#I/ꮵ+PwqW\ڼ6k,$c"hجQ1`JB>UY/$1]y)mhc(x(TbЃOE[$}Ig=#H"X*!Q=3fnYŨҿR A}>[s)R)/J.IT9|K PGbncUHudyc=("Dn*1 E+Mmc.-RRL.ebhT*K=& aYQb.-/x.{!Rȇs\UzlfW\ 7RrдgiPC<-KOO@ҡQz| 9it}VK&NO) КŨ$:-Osooo^xPp7*|_#MH~,yveAF?Y.=Rxڿ,UZì&/U Y!HTFXU~ouX HSLj w6HbQUiKS ֮z4B˲Io빨$G^,1W]wZipmF+I⹊k eoOX튥v ;Miwe:FGUb?SWo,C}n{oxވtgO,qCxN* +ˤi+k,7 ,!X#Hۜ ) 튫}V+[F 㽼^46֫p3e +!*OnncF6Pe2 =9%yoGupH] kړj0bJ[C5-!Zn7SD{owgM;Z kH{%0GkMSK%ԙ-Vcm0ysu-DI 9b{ 4tVhwPlS;Ck=UXz1[h~$Vw \UknUőXJ~1OA5A"]ipYO6hY]YH +Yˉ9o,գHr`@7Y̚ 彔W ;<ȭvJfHh'jGRK5^]Mu% ?'*!mVD$|z}n.73*P"WҭntЭm_\L,0px Uh+naz|01KGrIx(Zȴ ׊&Sui>7]S,q2fF5k*߲3]mA*'%C/N? GzZ|rk{vgm=3A`td!xPK-X..G;Q*~a0$Vks!+ °zCclx4彶NeWȸ0YT5\UR uTvVIeLtj`N*eqjW UY$).*ZOX-ɬf#L7sۈŻ3?EhIvh\Ƭ޴4SXL}>;XT%FE4f\? N}H>8UB;y-a,7\ #1p3qH,lo=TaXƭ aWiMhqUI-,.l{{ =*5@*D?y@kb-,Kz|XkB _QH(j Uk2k}_Oh`Ӧ pZ}*Mzr늦t:inm]>KG06,Cc3z ˑބP0Xiֶ7}{:bhcV%Jq|HzWTgԴO濹ҵhu 6BKF<söت";kju$YZԊ #2U\ʫBi!ѤcD'qF.kΝf4 9cyXeeaኢDZ, $B*W9SlURDҵuqMi.iCoDV_B72կWČU%!JI :,5ɾI2+XrDԍ Y՚ "ԪU(n9%ԓֺkɨ/ [T3aZu*)L徠9f$-/ipg#q1DZQl$[z׺ZViO,IUJ<* x)"C*-  U1zŞݥT_D8F Fu{oUѽCɅcbNһW#EcX99@ZVS U"b%k?U狙[)~(:h:oLU:+g 4䲝mE7+C&Qy>Xfotoxx-15.11.1/data/patterns/canvas2.jpg0000644000175000017500000002105512616075370016471 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-15.11.1/data/patterns/pattern_114.jpg0000644000175000017500000002711112616075370017175 0ustar micomicoJFIF 3http://ns.adobe.com/xap/1.0/ 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-15.11.1/data/patterns/burlap.jpg0000644000175000017500000006112512616075370016423 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-15.11.1/data/retouch_combo/0000755000175000017500000000000012616075370015417 5ustar micomicofotoxx-15.11.1/data/retouch_combo/rich0000644000175000017500000000117012616075370016266 0ustar micomicoall == 1 red == 0 green == 0 blue == 0 amplify == 1 brightness == 0 contrast == 0.109589 satlevel == 0.315068 wbtemp == 4609.78 areaemph == 0 dist == 0 click == 0 curves 5 0 10 0.087/0.005 0.236/0.255 0.497/0.540 0.680/0.685 0.792/0.758 0.845/0.783 0.916/0.827 0.953/0.882 0.981/0.933 1.000/0.980 0 10 0.087/0.005 0.236/0.255 0.497/0.540 0.680/0.685 0.792/0.758 0.845/0.783 0.916/0.827 0.953/0.882 0.981/0.933 1.000/0.980 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 fotoxx-15.11.1/data/retouch_combo/darken low0000644000175000017500000000075712616075370017401 0ustar micomicoall == 1 red == 0 green == 0 blue == 0 amplify == 1 brightness == 0 contrast == 0 satlevel == 0 wbtemp == 5000 areaemph == 0.5 dist == 0 click == 0 curves 5 0 5 0.000/0.000 0.156/0.032 0.453/0.389 0.750/0.750 0.990/0.990 0 5 0.000/0.000 0.156/0.032 0.453/0.389 0.750/0.750 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 fotoxx-15.11.1/data/retouch_combo/level0000644000175000017500000000065012616075370016452 0ustar micomicoall == 1 red == 0 green == 0 blue == 0 amplify == 1 brightness == 0 contrast == 0 satlevel == -1 wbtemp == 5000 areaemph == 0.5 dist == 0 click == 0 curves 5 0 2 0.510/0.000 0.527/0.994 0 2 0.510/0.000 0.527/0.994 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 fotoxx-15.11.1/data/retouch_combo/soften0000644000175000017500000000111512616075370016636 0ustar micomicoall == 1 red == 0 green == 0 blue == 0 amplify == 1 brightness == 0 contrast == 0 satlevel == 0 wbtemp == 5000 areaemph == 0.5 dist == 0 click == 0 curves 5 0 9 0.000/0.000 0.176/0.250 0.354/0.501 0.501/0.673 0.747/0.818 0.881/0.881 0.958/0.932 0.990/0.976 1.000/1.000 0 9 0.000/0.000 0.176/0.250 0.354/0.501 0.501/0.673 0.747/0.818 0.881/0.881 0.958/0.932 0.990/0.976 1.000/1.000 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 fotoxx-15.11.1/data/retouch_combo/unclip0000644000175000017500000000111612616075370016633 0ustar micomicoall == 1 red == 0 green == 0 blue == 0 amplify == 1 brightness == 0 contrast == 0 satlevel == 0 wbtemp == 5000 areaemph == 0.5 dist == 0 click == 0 curves 5 0 9 0.010/0.010 0.250/0.250 0.500/0.500 0.677/0.679 0.772/0.741 0.890/0.791 0.936/0.851 0.990/0.980 1.000/0.999 0 9 0.010/0.010 0.250/0.250 0.500/0.500 0.677/0.679 0.772/0.741 0.890/0.791 0.936/0.851 0.990/0.980 1.000/0.999 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 fotoxx-15.11.1/data/retouch_combo/lighten low0000644000175000017500000000103612616075370017556 0ustar micomicoall == 1 red == 0 green == 0 blue == 0 amplify == 1 brightness == 0 contrast == 0 satlevel == 0 wbtemp == 5000 areaemph == 0.5 dist == 0 click == 0 curves 5 0 6 0.009/0.074 0.047/0.129 0.242/0.331 0.472/0.479 0.739/0.718 0.990/0.980 1.000/1.000 0 6 0.009/0.074 0.047/0.129 0.242/0.331 0.472/0.479 0.739/0.718 0.990/0.980 1.000/1.000 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 fotoxx-15.11.1/data/retouch_combo/lighten high0000644000175000017500000000064712616075370017703 0ustar micomicoall == 1 red == 0 green == 0 blue == 0 amplify == 1 brightness == 0 contrast == 0 satlevel == 0 wbtemp == 5000 areaemph == 0.5 dist == 0 click == 0 curves 5 0 5 0.000/0.000 0.183/0.221 0.491/0.613 0.795/0.945 1.000/1.000 0 5 0.000/0.000 0.183/0.221 0.491/0.613 0.795/0.945 1.000/1.000 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 0 2 0.000/0.000 0.990/0.990 0 2 0.010/0.010 1.000/1.000 fotoxx-15.11.1/data/retouch_combo/darken high0000644000175000017500000000103712616075370017507 0ustar micomicoall == 1 red == 0 green == 0 blue == 0 amplify == 1 brightness == 0 contrast == 0 satlevel == 0 wbtemp == 5000 areaemph == 0.5 dist == 0 click == 0 curves 5 0 7 0.010/0.010 0.250/0.250 0.522/0.470 0.788/0.676 0.919/0.795 0.972/0.919 0.990/0.990 0 7 0.010/0.010 0.250/0.250 0.522/0.470 0.788/0.676 0.919/0.795 0.972/0.919 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 0 5 0.010/0.010 0.250/0.250 0.500/0.500 0.750/0.750 0.990/0.990 fotoxx-15.11.1/data/retouch_combo/forest0000644000175000017500000000076512616075370016654 0ustar micomicoall == 1 red == 0 green == 0 blue == 0 amplify == 1 brightness == 0.0706522 contrast == 0.398551 satlevel == 0.181159 wbtemp == 4449.28 areaemph == 0.518116 dist == 0 click == 0 curves 5 0 5 0.064/0.000 0.252/0.200 0.500/0.525 0.750/0.867 1.000/0.995 0 5 0.064/0.000 0.252/0.200 0.500/0.525 0.750/0.867 1.000/0.995 0 5 0.010/0.010 0.223/0.232 0.445/0.490 0.783/0.801 0.990/0.990 0 3 0.010/0.010 0.509/0.529 0.990/0.990 0 5 0.000/0.000 0.257/0.191 0.489/0.493 0.686/0.796 0.990/0.990 fotoxx-15.11.1/data/edit-menus-en0000644000175000017500000001107112616075370015164 0ustar micomico Edit ==== Trim/Rotate Trim unwanted margins and/or rotate an image. Upright Upright an image rotated 90° or 180°. Voodoo1 Automatic 1-click enhancement that may work. Voodoo2 Automatic 1-click enhancement that may work. Retouch Combo Edit brightness, contrast, color, saturation. Adjust Bright. Dist. Flatten or expand brightness distribution. Zonal Flatten Flatten zonal brightness distribution. Tone Mapping Increase local contrast to to enhance details. Resize Contract or Enarge an image (pixel dimensions). Flip Mirror an image horizontally or vertically. Add Text Write text on image (font, size, transparency). Add Lines Add lines and arrows to the image. Paint Edits Paint a retouch edit gradually using the mouse. Leverage Edits Regulate a retouch edit with brightness or color. Plugins Use other image edit app as Fotoxx edit function Repair ====== Sharpen Sharpen a blurred image. Blur Blur an image (e.g. smoothen skin). Denoise Reduce noise (speckles) in low-light images. Smart Erase Remove power lines, trash, other spoilers. Red Eyes Remove red eyes from flash photos. Paint/Clone Mouse-paint using a color or source image area. Paint Transparency Paint [semi-] transparent areas onto an image. Color Mode Make black & white, negative/positive, sepia. Shift Colors Gradually shift RGB colors into other colors. Adjust RGB/CMY Adjust image color using RGB/CMY colors Adjust HSL Adjust image color using HSL colors Brightness Ramp Horizontal/Vertical variation of brightness. Color Ramp Make color corrections that vary over the image. Match Colors Match the colors in one image to another image. Color Profile Convert color profile (e.g. sRGB <> Adobe RGB). Remove Dust Remove spots on images scanned from dusty slides. Anti-Alias Remove pixelation (jaggies) in low resolution image. Color Fringes Reduce chromatic abberation causing color fringes. Stuck Pixels Fix bright/dark pixels from camera sensor defects. Bend ==== Unbend Fix perspective problems (esp. panoramas). Fix Perspective Straighten an object photographed from an angle. Warp area Distort "rubber" image by pulling with the mouse. Warp curved Distort entire image by pulling with the mouse. Warp linear Distort entire image by pulling with the mouse. Warp affine Distort entire image by pulling with the mouse. Flatten Book Page Flatten/straighten a photographed book page. Effects ======= Color Depth Reduce the number of colors (posterize). Sketch Convert photo to simulated pencil sketch. Line Drawing Convert photo to simulated line drawing. Color Drawing Convert photo to simulated color drawing. Graduated Blur Graduated blur depending on contrast. Embossing Convert photo to simulated embossing (3D effect). Tiles Convert photo to simulated square tiles. Dots Convert photo to Roy Lichtenstein graphic. Painting Convert photo to simulated painting. Vignette Change brightness or color in a radial pattern. 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. Make Waves Warp an image with a wavy pattern. Directed Blur Blur an image in a single direction via mouse drag. Spherical Projection Project an image onto a sphere, variale flatness. Combine ======= 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 with Panorama Tools. Mashup Arrange multiple images and formatted text in a layout. fotoxx-15.11.1/data/favorites/0000755000175000017500000000000012616075370014571 5ustar micomicofotoxx-15.11.1/data/favorites/menu-config0000644000175000017500000000063712616075370016731 0ustar micomicopopup 120 42 204 117 posn 20 36 menu trim/rotate func Trim/Rotate posn 20 4 menu recent func Recently Seen Images posn 20 20 menu previous func Open Previous File posn 100 4 menu combo func Retouch Combo posn 100 20 menu bright dist func Adjust Brightness Dist. posn 100 52 menu tonemap func Tone Mapping posn 100 68 menu sharpen func Sharpen posn 20 68 menu red eyes func red eyes fotoxx-15.11.1/data/KB-shortcuts-es0000644000175000017500000000031612616075370015447 0ustar micomicoN Renombrar imagen K Atajos de teclado Alt+G Líneas de rejilla T Recortar imagen V Mejora automática R Retocar luz y color U Deshacer Shift+U Rehacer P Abrir archivo anterior fotoxx-15.11.1/f.image.cc0000664000175000017500000011754012616075370013505 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** Fotoxx image edit - graphic image functions vpixel get virtual pixel at any 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 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 // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are defined) /**************************************************************************/ // 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; // 15.08 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) 15.08 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; // 15.08 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) // 15.08 { uint cc; if (ww < 5 || hh < 5) { // impose reasonableness limits zmessageACK(Mwin,"image too small: %d %d",ww,hh); return 0; } cc = ww * hh; if (ww > wwhh_limit || hh > wwhh_limit || cc > 178000000) { // avoid image > 2 GB memory 15.03 zmessageACK(Mwin,"image too big: %d %d",ww,hh); exit(12); } 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 15.08 cc = cc * nc * sizeof(float); // float[nc] per pixel pxm->pixels = (float *) zmalloc(cc); 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) // 15.08 { int ww, hh, nc1, nc2, cc, 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; 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) { int cc, nc; PXM *pxm2; nc = pxm1->nc; cc = pxm1->ww * pxm1->hh * nc * sizeof(float); pxm2 = PXM_make(pxm1->ww,pxm1->hh,nc); // 15.08 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; // 15.08 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; // 15.08 ppix1 = pxm1->pixels; pxm2 = PXM_make(ww,hh,nc); // output PXM if (! pxm2) return 0; ww2 = ww; hh2 = hh; ppix2 = pxm2->pixels; memset(ppix2, 0, ww2 * hh2 * 3 * sizeof(float)); // clear output pixmap 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.004); 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 15.08 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) { using namespace pxmrotate; PXM *PXM_rotate2(PXM *pxm, int angle); void *PXM_rotate_thread(void *); int ii; PXM *pxm2; ww1 = pxm1->ww; // input PXM hh1 = pxm1->hh; nc = pxm1->nc; // 15.08 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 ppix2 = pxm2->pixels; 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.004); // 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); // 15.08 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; } 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; // 15.08 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) // 15.08 { zthreadcrash(); 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 15.08 int 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) { zthreadcrash(); 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); // 15.08 pxb2->pixels = gdk_pixbuf_get_pixels(pixbuf1); return pxb2; } // Free PXB pixmap - release memory void PXB_free(PXB *&pxb) { zthreadcrash(); 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; // 15.08 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 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) { zthreadcrash(); 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; // 15.08 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; zthreadcrash(); pixbuf = gdk_pixbuf_scale_simple(pxb1->pixbuf,ww2,hh2,GDK_INTERP_BILINEAR); if (! pixbuf) zappcrash("memory allocation failure"); PXB *pxb2 = PXB_make(pixbuf); return pxb2; } // Rotate PXB pixmap by given angle. PXB * PXB_rotate(PXB *pxb1, float angle) // 15.04 { PIXBUF *pixbuf = 0; zthreadcrash(); pixbuf = gdk_pixbuf_rotate(pxb1->pixbuf,angle); if (! pixbuf) zappcrash("memory allocation failure"); 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]; // 15.08 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; 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 15.08 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; // 15.08 } } 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; // 15.08 ppix1 = pxb1->pixels; ww2 = pxb2->ww; hh2 = pxb2->hh; rs2 = pxb2->rs; nc2 = pxb2->nc; // 15.08 ppix2 = pxb2->pixels; if (nc1 > 3 && nc2 > 3) ac = 1; // alpha channel 15.08 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 15.08 pixel2[0] = red; // 0.0 - 255.996 >> 0 - 255 pixel2[1] = green; pixel2[2] = blue; if (ac) pixel2[3] = alpha; } } return; } /**************************************************************************/ // fast fixbuf rescale, bilinear interpolation, threaded // works best when the rescale ratio is between 0.5 and 2.0 // about 4x faster than gdk_pixbuf_scale_simple() if 4 processor cores avail. // 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]; } PIXBUF * pixbuf_rescale_fast(PIXBUF *pixbuf1, int ww, int hh) // 15.02 { using namespace pixbuf_rescale_fast_names; void * pixbuf_rescale_fast_thread(void *arg); PIXBUF *pixbuf2; uint8 *pix2; pixbuf2 = gdk_pixbuf_new(GDKRGB,0,8,ww,hh); 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); // 15.08 pixels1 = gdk_pixbuf_get_pixels(pixbuf1); ww2 = ww; hh2 = hh; rs2 = gdk_pixbuf_get_rowstride(pixbuf2); nc2 = gdk_pixbuf_get_n_channels(pixbuf2); // 15.08 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; } fotoxx-15.11.1/desktop0000644000175000017500000000153312616075370013254 0ustar micomico[Desktop Entry] Name=Fotoxx GenericName=Photo Editor GenericName[es]=Editor fotográfico GenericName[gl]=Editor fotográfico Comment=Edit photos and manage collections Comment[es]=Editar fotografías y administrar colecciones Comment[gl]=Editar fotografías e administrar coleccións Categories=Graphics;Photography; 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 Keywords=photo;image;raw;editor;exif;collections; X-Ayatana-Desktop-Shortcuts=X-blank window;X-last image;X-recent images [X-blank window Shortcut Group] Name=blank window Exec=fotoxx -b TargetEnvironment=Unity [X-last image Shortcut Group] Name=last image Exec=fotoxx -p TargetEnvironment=Unity [X-recent images Shortcut Group] Name=recent images Exec=fotoxx -r TargetEnvironment=Unity fotoxx-15.11.1/f.repair.cc0000664000175000017500000111334312616075370013703 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** Fotoxx image edit - Repair menu functions m_sharpen sharpen an image m_blur blur an image m_denoise remove noise from an image m_smart_erase replace pixels inside selected areas with background m_redeye remove red-eyes from flash photos m_paint_clone paint with an RGB color or with pixels from another area m_paint_transparency paint more or less transparency on an image m_color_mode convert between color, B&W, sepia, positive, negative m_shift_colors gradually shift selected RGB colors into other colors m_adjust_RGB adjust brightness/color using RGB or CMY colors m_adjust_HSL adjust color using HSL model m_brightramp adjust brightness graduaally across the image m_color_ramp shift RGB colors in selected image areas m_match_color adjust image colors to match those in a chosen image m_color_profile convert from one color profile to another m_remove_dust remove dust specs from an image m_anti_alias suppress pixel steps (jaggies) from image edges m_color_fringes stretch/shrink RGB color planes to remove color fringes m_stuck_pixels find and fix stuck pixels from camera sensor defects ***************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /**************************************************************************/ // image sharpen functions namespace sharpen { int UM_radius; int UM_amount; int UM_thresh; int GR_amount; int GR_thresh; int KH_radius; char sharp_function[4] = ""; volatile // compiler bug: thread references int sharp_cancel; // to variable are removed *** 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 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 15.10 if (! edit_setup(EFsharp)) return; // setup edit /*** _______________________________________ | Sharpen | | | | [] unsharp mask radius [__|-+] | | amount [__|-+] | | threshold [__|-+] | | | | [] gradient amount [__|-+] | | threshold [__|-+] | | | | [] Kuwahara radius [__|-+] | | | | [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"); zdialog_add_widget(zd,"vbox","vb21","hb2",0,"space=2"); 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,"spin","radiusUM","vb23","1|20|1|2"); zdialog_add_widget(zd,"spin","amountUM","vb23","1|200|1|100"); zdialog_add_widget(zd,"spin","threshUM","vb23","1|100|1|0"); zdialog_add_widget(zd,"hsep","sep3","dialog"); 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,"space=10"); 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,"spin","amountGR","vb33","1|400|1|100"); zdialog_add_widget(zd,"spin","threshGR","vb33","1|100|1|0"); zdialog_add_widget(zd,"hsep","sep4","dialog"); 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,"space=10"); zdialog_add_widget(zd,"label","lab42","hb4",Bradius,"space=3"); zdialog_add_widget(zd,"spin","radiusKH","hb4","1|9|1|1"); zdialog_restore_inputs(zd); zdialog_fetch(zd,"UM",ii); // set function from checkboxes 15.10 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_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 15.10 { 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); if (strmatch(event,"apply")) zd->zstat = 2; // from script file 15.10 if (strmatch(event,"done")) zd->zstat = 3; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 3; // KB input if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open() if (strmatch(event,"escape")) zd->zstat = 4; // escape = cancel 15.07 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(); return 1; } if (zd->zstat == 3) { edit_done(0); // done return 1; } sharp_cancel = 1; edit_cancel(0); // discard edit return 1; } if (strmatch(event,"blendwidth")) signal_thread(); if (strmatchV(event,"UM","GR","KH",null)) { zdialog_stuff(zd,"UM",0); // make checkboxes like radio buttons zdialog_stuff(zd,"GR",0); zdialog_stuff(zd,"KH",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(); 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(); 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 = E1pxm->ww * E1pxm->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 = E3pxm->ww * E3pxm->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 < E3pxm->hh; py += NWT) // loop all image3 pixels 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; // 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 * E1pxm->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 = 1.0 * dist / sa_blend; // 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 = E3pxm->ww * E3pxm->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; // 15.09 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 < E1pxm->hh; py += NWT) // loop all image pixels for (px = 1; 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; // 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 15.09 b1y = b1 - pixbright(pix1-nc * E1pxm->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 * E3pxm->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 = 1.0 * dist / sa_blend; // 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 = E3pxm->ww * E3pxm->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 < E1pxm->hh-rad; py += NWT) // loop all image pixels for (px = rad; px < E1pxm->ww-rad; px++) { if (sa_stat == 3) { // select area active ii = py * E1pxm->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 = 1.0 * dist / sa_blend; // 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 } // 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 = E1pxm->ww * E1pxm->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 < E1pxm->hh; py += NWT) for (px = 0; px < E1pxm->ww; px++) { if (sa_stat == 3 && sa_mode != mode_image) { // select area, not whole image ii = py * E1pxm->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 > E1pxm->hh-1) continue; if (qx < 0 || qx > E1pxm->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 * E1pxm->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 < E1pxm->hh; py += NWT) for (px = 0; px < E1pxm->ww; px++) { if (sa_stat == 3 && sa_mode != mode_image) { // select area, not whole image ii = py * E1pxm->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 > E1pxm->hh-1) continue; if (qx < 0 || qx > E1pxm->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 > E1pxm->hh-1) continue; pixel = PXMpix(E1pxm,qx,qy); bsum -= pixbright(pixel); bsamp -= 1; } } qx = px+rad; // add last column if (qx < E1pxm->ww) { for (qy = py-rad; qy <= py+rad; qy++) { if (qy < 0 || qy > E1pxm->hh-1) continue; pixel = PXMpix(E1pxm,qx,qy); bsum += pixbright(pixel); bsamp += 1; } } } bmean = bsum / bsamp; // mean brightness ii = py * E1pxm->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; int blur_cancel; char *blur_flags; 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 15.10 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,"spin","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,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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; // 15.09 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; // 15.09 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 = 1.0 * dist / sa_blend; // 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_methods { minmax, stdev, median, tophat, waves, thresh } 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; } // 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_image"; 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 15.10 if (! edit_setup(EFdenoise)) return; // setup edit ww = E3pxm->ww; // image dimensions hh = E3pxm->hh; /*** _____________________________________________ | Noise Reduction | | Apply repeatedly while watching the image. | | | | [_] Flatten 1 Radius [___+|-] | | [_] Flatten 2 Radius [___+|-] | | [_] Median Radius [___+|-] | | [_] Top Hat Radius [___+|-] | | [x] Wavelets Threshold [___+|-] | | [_] Threshold 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,"check","minmax","vb1"); zdialog_add_widget(zd,"check","stdev","vb1"); zdialog_add_widget(zd,"check","median","vb1"); zdialog_add_widget(zd,"check","tophat","vb1"); zdialog_add_widget(zd,"check","waves","vb1"); zdialog_add_widget(zd,"check","thresh","vb1"); zdialog_add_widget(zd,"label","labminmax","vb2",ZTX("Flatten 1")); zdialog_add_widget(zd,"label","labstdev","vb2",ZTX("Flatten 2")); 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","labthreshold","vb2",Bthresh); zdialog_add_widget(zd,"label","labradminmax","vb3",Bradius); zdialog_add_widget(zd,"label","labradstdev","vb3",Bradius); zdialog_add_widget(zd,"label","labradmedian","vb3",Bradius); zdialog_add_widget(zd,"label","labradtophat","vb3",Bradius); zdialog_add_widget(zd,"label","labwavesthresh","vb3",Bthresh); zdialog_add_widget(zd,"label","labthreshthresh","vb3",Bthresh); zdialog_add_widget(zd,"spin","radminmax","vb4","1|9|1|1"); zdialog_add_widget(zd,"spin","radstdev","vb4","1|9|1|3"); zdialog_add_widget(zd,"spin","radmedian","vb4","1|9|1|2"); zdialog_add_widget(zd,"spin","radtophat","vb4","1|9|1|4"); zdialog_add_widget(zd,"spin","wavesthresh","vb4","0.0|8|0.1|1"); zdialog_add_widget(zd,"spin","threshthresh","vb4","0|50|1|3"); 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 15.10 { using namespace denoise; void denoise_measure(); int ii; 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,"enter")) zd->zstat = 4; // KB input if (strmatch(event,"cancel")) zd->zstat = 5; // from f_open() if (strmatch(event,"escape")) zd->zstat = 5; // escape = cancel 15.07 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) { // [done] commit edit edit_done(0); return 1; } edit_cancel(0); // [cancel] or [x] discard edit if (zd_denoise_measure) { 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("minmax stdev median tophat waves thresh",event)) { // checkboxes work like radio buttons zdialog_stuff(zd,"minmax",0); zdialog_stuff(zd,"stdev",0); zdialog_stuff(zd,"median",0); zdialog_stuff(zd,"tophat",0); zdialog_stuff(zd,"waves",0); zdialog_stuff(zd,"thresh",0); zdialog_stuff(zd,event,1); } zdialog_fetch(zd,"minmax",ii); if (ii) { denoise_method = minmax; zdialog_fetch(zd,"radminmax",denoise_radius); } zdialog_fetch(zd,"stdev",ii); if (ii) { denoise_method = stdev; zdialog_fetch(zd,"radstdev",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,"waves",ii); if (ii) { denoise_method = waves; zdialog_fetch(zd,"wavesthresh",denoise_thresh); } zdialog_fetch(zd,"thresh",ii); if (ii) { denoise_method = thresh; zdialog_fetch(zd,"threshthresh",denoise_thresh); } return 1; } // dialog to measure noise at mouse click position void denoise_measure() // 15.06 { using namespace denoise; int denoise_measure_dialog_event(zdialog *zd, cchar *event); char text[100]; cchar *title = ZTX("Measure Noise"); cchar *clickmess = ZTX("Click on a monotone image area."); /*** ___________________________________ | Measure Noise | | Click on a monotone image area. | | | | 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); // smartdenoise dialog zd_denoise_measure = zd; zdialog_add_widget(zd,"label","clab","dialog",clickmess,"space=3"); 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_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,"escape")) zd->zstat = 1; // escape = cancel 15.07 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() // improved 15.07 { using namespace denoise; char text[100]; int mx, my, px, py, qx, qy, Npix; float *pix3, R; float Rm, Gm, Bm, Rn, Gn, Bn; zdialog *zd = zd_denoise_measure; draw_mousecircle(Mxposn,Myposn,10,0); // draw mouse circle, radius 10 if (! LMclick) return; // wait for mouse click LMclick = 0; 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); mx = Mxclick; // mouse click position my = Myclick; if (mx < 13 || mx >= ww-13) return; // must be 12+ pixels from image edge if (my < 13 || my >= hh-13) return; Npix = 0; Rn = Gn = Bn = 0; for (py = my-10; py <= my+10; py++) // get pixels around mouse position for (px = mx-10; px <= mx+10; px++) // within a radius of 10 { // (approx. 314 pixels) R = sqrtf((px-mx)*(px-mx) + (py-my)*(py-my)); if (R > 10) continue; Npix++; Rm = Gm = Bm = 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); Rm += pix3[0]; Gm += pix3[1]; Bm += pix3[2]; } Rm = Rm / 25; // mean RGB for surrounding pixels Gm = Gm / 25; Bm = Bm / 25; pix3 = PXMpix(E3pxm,px,py); // get pixel RGB noise levels Rn += fabsf(Rm - pix3[0]); // = | pixel RGB - mean RGB | Gn += fabsf(Gm - pix3[1]); // accumulate noise levels Bn += fabsf(Bm - pix3[2]); } Rn = Rn / Npix; // mean RGB noise levels Gn = Gn / Npix; // for pixels around mouse Bn = Bn / Npix; Npix = 0; Rm = Gm = Bm = 0; for (py = my-10; py <= my+10; py++) // get pixels around mouse position for (px = mx-10; px <= mx+10; px++) // within a radius of 10 { // (approx. 314 pixels) R = sqrtf((px-mx)*(px-mx) + (py-my)*(py-my)); if (R > 10) continue; Npix++; pix3 = PXMpix(E3pxm,px,py); // get pixel RGB values Rm += pix3[0]; // accumulate Gm += pix3[1]; Bm += pix3[2]; } Rm = Rm / Npix; // mean RGB values Gm = Gm / Npix; // for pixels around mouse Bm = Bm / Npix; 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); return; } // image noise reduction thread void * denoise_thread(void *) { using namespace denoise; void * denoise_wthread(void *arg); void * denoise_wavelet_wthread(void *arg); void denoise_threshold(); int ii, px, py, dist = 0; float *pix3, *pix9; int nc = E3pxm->nc, pcc = nc * sizeof(float); // 15.09 while (true) { thread_idle_loop(); // wait for work or exit request E9pxm = PXM_copy(E3pxm); // image3 is source, image9 is modified if (denoise_method == waves) // 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 if (denoise_method == thresh) // do threshold denoise denoise_threshold(); // (multiple worker threads) 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, 15.01 { // 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); } } if (! resource_lock(Fpaintlock)) continue; // 15.02 PXM_free(E3pxm); // image9 >> image3 E3pxm = E9pxm; E9pxm = 0; resource_unlock(Fpaintlock); 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_minmax(float *pix3, float *pix9); void denoise_stdev(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 == minmax) denoise_minmax(pix3,pix9); if (denoise_method == stdev) denoise_stdev(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 = 1.0 * dist / sa_blend; // 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 outliers within radius, by color. // An outlier is the max or min RGB value within a radius. void denoise_minmax(float *pix3, float *pix9) { using namespace denoise; int dy, dx, rad; int minR, minG, minB, maxR, maxG, maxB; float *pixN; int nc = E3pxm->nc; // 15.09 minR = minG = minB = 255; maxR = maxG = maxB = 0; rad = denoise_radius; 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; // 15.09 if (pixN[0] < minR) minR = pixN[0]; // find min and max per color if (pixN[0] > maxR) maxR = pixN[0]; 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 && minR < 255) pix9[0] = minR + 1; // if outlier, flatten a little if (pix3[0] >= maxR && maxR > 1) pix9[0] = maxR - 1; if (pix3[1] <= minG && minG < 255) pix9[1] = minG + 1; if (pix3[1] >= maxG && maxG > 1) pix9[1] = maxG - 1; if (pix3[2] <= minB && minB < 255) pix9[2] = minB + 1; if (pix3[2] >= maxB && maxB > 1) pix9[2] = maxB - 1; return; } // ----------------------------------------------------------------------- // Flatten outliers. // An outlier pixel has an RGB value outside one sigma of // the mean for all pixels within a given radius. void denoise_stdev(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; // 15.09 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; // 15.09 val = pixN[rgb]; sum += val; sum2 += val * val; } mean = nn1 * sum; variance = nn1 * (sum2 - 2.0 * mean * sum) + mean * mean; sigma = sqrtf(variance); val = pix3[rgb]; if (val > mean + sigma) { // move value to mean +/- sigma val = mean + sigma; pix9[rgb] = val; } else if (val < mean - sigma) { val = mean - sigma; pix9[rgb] = val; } } return; } // ----------------------------------------------------------------------- // 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; // 15.09 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; // 15.09 bsortN[ns] = pixN[rgb]; ns++; } HeapSort(bsortN,ns); pix9[rgb] = bsortN[ns/2]; // median brightness of ns pixels } return; } // ----------------------------------------------------------------------- // Modified top hat filter: 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; // 15.09 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; // 15.09 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 (minR + minG + minB > 3 * denoise_darkareas) continue; // skip brighter areas 15.01 if (pix3[0] < minR && pix9[0] < 255) pix9[0]++; // if central pixel is outlier, if (pix3[0] > maxR && pix9[0] > 1) pix9[0]--; // moderate its values if (pix3[1] < minG && pix9[1] < 255) pix9[1]++; if (pix3[1] > maxG && pix9[1] > 1) pix9[1]--; if (pix3[2] < minB && pix9[2] < 255) pix9[2]++; if (pix3[2] > maxB && pix9[2] > 1) pix9[2]--; } return; } // ----------------------------------------------------------------------- // 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; // 15.09 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; // 15.09 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; // 15.09 } 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 = 1.0 * dist / sa_blend; 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; } // ----------------------------------------------------------------------- // threshold denoise function void denoise_threshold() // 15.06 { using namespace denoise; void * denoise_threshold_wthread1(void *arg); // worker threads void * denoise_threshold_wthread2(void *arg); int cc, ii, nn, px, py; cc = ww * hh; Fpix = (char *) zmalloc(cc); // memory for feature pixels Gpix = (char *) zmalloc(cc); memset(Fpix,0,cc); for (ii = 0; ii < NWT; ii++) // start worker threads 1 start_wthread(denoise_threshold_wthread1,&Nval[ii]); wait_wthreads(); // wait for completion memcpy(Gpix,Fpix,cc); for (py = 3; py < hh-3; py++) // loop all image pixels for (px = 3; px < ww-3; px++) { ii = py * ww + px; // consolidate feature pixel flags 15.10 nn = Fpix[ii-ww-1] + Fpix[ii-ww] + Fpix[ii-ww+1] // look at 3x3 pixel block + Fpix[ii-1] + Fpix[ii] + Fpix[ii+1] + Fpix[ii+ww-1] + Fpix[ii+ww] + Fpix[ii+ww+1]; if (nn < 3) Gpix[ii] = 0; if (nn > 4) Gpix[ii] = 1; } memcpy(Fpix,Gpix,cc); for (ii = 0; ii < NWT; ii++) // start worker threads 2 start_wthread(denoise_threshold_wthread2,&Nval[ii]); wait_wthreads(); // wait for completion zfree(Fpix); zfree(Gpix); return; } void * denoise_threshold_wthread1(void *arg) // worker thread 1 function { using namespace denoise; int index = *((int *) (arg)); int px, py; int ii, dist = 1; float *pix3; float *pixa, *pixb, *pixc, *pixd, *pixe, *pixf; float cont, f1, f2; float thresh = 10 * denoise_thresh; int nc = E3pxm->nc; // 15.09 for (py = index+3; py < hh-3; py += NWT) // loop all image pixels for (px = 3; px < ww-3; px++) { ii = py * ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix3 = PXMpix(E3pxm,px,py); // pixel being tested pixa = pix3-nc; // 3 pixels left in row 15.09 pixb = pix3-2*nc; pixc = pix3-3*nc; pixd = pix3+nc; // 3 pixels right in row pixe = pix3+2*nc; pixf = pix3+3*nc; f1 = pixa[0] + pixb[0] + pixc[0]; // detect a threshold centered f2 = pixd[0] + pixe[0] + pixf[0]; // on this pixel cont = fabsf(f1-f2); if (cont > thresh) { Fpix[ii] = 1; // flag pixel - a likely feature edge continue; } f1 = pixa[1] + pixb[1] + pixc[1]; f2 = pixd[1] + pixe[1] + pixf[1]; cont = fabsf(f1-f2); if (cont > thresh) { Fpix[ii] = 1; continue; } f1 = pixa[2] + pixb[2] + pixc[2]; f2 = pixd[2] + pixe[2] + pixf[2]; cont = fabsf(f1-f2); if (cont > thresh) { Fpix[ii] = 1; continue; } pixa = pix3-ww*nc; // 3 pixels in column above 15.09 pixb = pix3-ww*2*nc; pixc = pix3-ww*3*nc; pixd = pix3+ww*nc; // 3 pixels in column below pixe = pix3+ww*2*nc; pixf = pix3+ww*3*nc; f1 = pixa[0] + pixb[0] + pixc[0]; // repeat threshold detection f2 = pixd[0] + pixe[0] + pixf[0]; cont = fabsf(f1-f2); if (cont > thresh) { Fpix[ii] = 1; continue; } f1 = pixa[1] + pixb[1] + pixc[1]; f2 = pixd[1] + pixe[1] + pixf[1]; cont = fabsf(f1-f2); if (cont > thresh) { Fpix[ii] = 1; continue; } f1 = pixa[2] + pixb[2] + pixc[2]; f2 = pixd[2] + pixe[2] + pixf[2]; cont = fabsf(f1-f2); if (cont > thresh) { Fpix[ii] = 1; continue; } } exit_wthread(); // exit thread return 0; // not executed, avoid gcc warning } void * denoise_threshold_wthread2(void *arg) // worker thread 2 function { using namespace denoise; int index = *((int *) (arg)); int px, py, qx, qy; int ii, jj, npix, dist = 1; int rad, done[7][7]; float *pix3, *pix9, T; float R1, G1, B1, R3, G3, B3; float f1, f2; for (py = index+4; py < hh-4; py += NWT) // loop all image pixels for (px = 4; px < ww-4; px++) { ii = py * ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix3 = PXMpix(E3pxm,px,py); // pixel being tested R1 = pix3[0]; G1 = pix3[1]; B1 = pix3[2]; if (Fpix[ii]) // flagged as a feature { R3 = R1; // copy pixel to output G3 = G1; B3 = B1; } else // not a feature { for (qx = -3; qx <= 3; qx++) // mark all neighbor pixels for (qy = -3; qy <= 3; qy++) // "not done" done[qx+3][qy+3] = 0; npix = 0; // replace pixel RGB values R3 = G3 = B3 = 0; // with neighborhood means for (T = 0; T < 2*PI; T += 0.15) // loop angle 0 - 360 deg. 15.10 for (rad = 0; rad <= 5; rad++) // loop radius 0 - 5 { qx = rad * cosf(T); // -3 ... +3 qy = rad * sinf(T); if (qx > 3 || qx < -3) continue; if (qy > 3 || qy < -3) continue; if (done[qx+3][qy+3]) continue; // skip if already done done[qx+3][qy+3] = 1; // mark done qx += px; qy += py; jj = qy * ww + qx; if (Fpix[jj]) break; // ran into feature pixel pix3 = PXMpix(E3pxm,qx,qy); R3 += pix3[0]; // accumulate RGB values G3 += pix3[1]; B3 += pix3[2]; npix++; } R3 = R3 / npix; // RGB means G3 = G3 / npix; B3 = B3 / npix; } if (sa_stat == 3 && dist < sa_blend) // select area active { f1 = 1.0 * dist / sa_blend; // blend changes over sa_blend f2 = 1.0 - f1; R3 = f1 * R3 + f2 * R1; G3 = f1 * G3 + f2 * G1; B3 = f1 * B3 + f2 * B1; } pix9 = PXMpix(E9pxm,px,py); // output pixel in E9 image pix9[0] = R3; pix9[1] = G3; pix9[2] = B3; } exit_wthread(); // exit thread return 0; // not executed, avoid gcc warning } /**************************************************************************/ // 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,"spin","radius","hb2","1|20|1|10"); zdialog_add_widget(zd,"label","labb","hb2",ZTX("Blur"),"space=10"); zdialog_add_widget(zd,"spin","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); 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,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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); takeMouse(sa_mouse_select,0); } if (strmatch(event,"show")) { sa_stat = 1; // status = active edit sa_show(1); takeMouse(sa_mouse_select,0); } if (strmatch(event,"hide")) { sa_show(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); } 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); // 15.09 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 15.03 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 15.03 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; // 15.09 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; } /**************************************************************************/ // 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,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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); // 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); // 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); // 15.09 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); // 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; } /**************************************************************************/ // Pixel paint function - paint individual pixels with the mouse. // The mouse circle pixels have a selected color, or they are taken from the // image at a relative position (x,y) from the mouse position (aka "clone"). #define pc_undomaxmen (2000 * MEGA) // max. undo memory #define pc_undomaxpix (MEGA) // max. pixel blocks namespace pclone_names { int pc_dialog_event(zdialog* zd, cchar *event); void pc_mousefunc(); void pc_dopixels(int px, int py); void pc_saveundo(int px, int py); void pc_undo1(); void pc_freeundo(); uint8 RGB[3] = { 255, 0, 0 }; int mode; // 1/2 = paint / erase int paint_mode; // 1/2 = paint color / paint image int Mradius; // mouse radius int imagex, imagey; // source image location float kernel[400][400]; // radius <= 199 int Fgradual = 1; // flag, gradual paint and unpaint/erase int undototpix = 0; // total undo pixel blocks int undototmem = 0; // total undo memory allocated int undoseq = 0; // undo sequence no. char undomemmessage[100]; // translated undo memory message char undomemformat[100]; typedef struct { // pixel block before edit int seq; // undo sequence no. uint16 px, py; // center pixel (radius org.) uint16 radius; // radius of pixel block float pixel[][3]; // array of pixel[npix][3] } savepix_t; int savepix_cc = 12; // all except pixel array + safety pad savepix_t **undopixmem = 0; // *savepix_t[] editfunc EFpaintclone; } // menu function void m_paint_clone(GtkWidget *, cchar *) { using namespace pclone_names; cchar *mess1 = ZTX("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"); F1_help_topic = "paint_clone"; EFpaintclone.menufunc = m_paint_clone; EFpaintclone.funcname = "paint_clone"; EFpaintclone.Farea = 2; // select area OK EFpaintclone.mousefunc = pc_mousefunc; // mouse function if (! edit_setup(EFpaintclone)) return; // setup edit /******** __________________________________________________ | Paint/Clone | | | | (o) paint color [_____] (o) copy from image | | | | left click: pick color or location from image | | left drag: paint color or image from location | | right drag: remove color or image | | | | paintbrush radius [___|v] [undo last] | | transparency center [___|v] [undo all] | | transparency edge [___|v] | | [x] gradual paint Undo Memory NN% | | | | [done] [cancel] | |__________________________________________________| ********/ zdialog *zd = zdialog_new(ZTX("Paint/Clone"),Mwin,Bdone,Bcancel,null); EFpaintclone.zd = zd; zdialog_add_widget(zd,"hbox","hbr","dialog",0,"space=3"); zdialog_add_widget(zd,"radio","paintcolor","hbr",ZTX("paint color"),"space=5"); zdialog_add_widget(zd,"colorbutt","colorbutt","hbr","255|0|0"); zdialog_add_widget(zd,"label","space","hbr",0,"space=10"); zdialog_add_widget(zd,"radio","paintimage","hbr",ZTX("copy from image")); 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("paintbrush radius")); zdialog_add_widget(zd,"label","labtc","vbbr1",ZTX("transparency center")); zdialog_add_widget(zd,"label","labte","vbbr1",ZTX("transparency edge")); zdialog_add_widget(zd,"spin","radius","vbbr2","1|199|1|20"); zdialog_add_widget(zd,"spin","trcent","vbbr2","0|100|0.1|95"); zdialog_add_widget(zd,"spin","tredge","vbbr2","0|100|0.1|100"); 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","gradual","hb4",ZTX("gradual paint"),"space=5"); zdialog_add_widget(zd,"label","labmem","hb4",0,"space=10"); zdialog_stuff(zd,"paintcolor",1); // default paint color mode zdialog_stuff(zd,"paintimage",0); zdialog_stuff(zd,"gradual",1); strncpy0(undomemformat,ZTX("Undo Memory %d%c"),99); snprintf(undomemmessage,99,undomemformat,0,'%'); // stuff undo memory status zdialog_stuff(zd,"labmem",undomemmessage); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,pc_dialog_event,"save"); // run dialog, parallel zdialog_fetch(zd,"paintcolor",paint_mode); // 1/2 = paint color / image if (paint_mode != 1) paint_mode = 2; zdialog_fetch(zd,"gradual",Fgradual); // instant/gradual paint zdialog_send_event(zd,"colorbutt"); // initialize paint color 15.07 zdialog_send_event(zd,"radius"); // get kernel initialized mode = 1; // start with paint mode undopixmem = 0; // no undo data undototpix = 0; undototmem = 0; undoseq = 0; imagex = imagey = 0; // no clone source pixels takeMouse(pc_mousefunc,drawcursor); // connect mouse function return; } // dialog event and completion callback function int pclone_names::pc_dialog_event(zdialog *zd, cchar *event) { using namespace pclone_names; char color[20]; cchar *pp; int radius, dx, dy; float rad, kern, trcent, tredge; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit pc_freeundo(); // free undo memory return 1; } draw_mousecircle(0,0,0,1); // erase mouse circle if (paint_mode == 2) draw_mousecircle2(0,0,0,1); // erase source tracking circle if (strmatch(event,"focus")) // toggle mouse capture takeMouse(pc_mousefunc,drawcursor); if (strstr("paintcolor paintimage",event)) { zdialog_fetch(zd,"paintcolor",paint_mode); // 1/2 = paint color / paint image if (paint_mode != 1) paint_mode = 2; } 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 (strstr("radius trcent tredge",event)) // get new brush attributes { zdialog_fetch(zd,"radius",Mradius); // mouse radius zdialog_fetch(zd,"trcent",trcent); // center transparency zdialog_fetch(zd,"tredge",tredge); // edge transparency trcent = 0.01 * trcent; // scale 0 ... 1 tredge = 0.01 * tredge; tredge = (1 - trcent) * (1 - tredge); tredge = 1 - tredge; trcent = sqrt(trcent); // speed up the curve tredge = sqrt(tredge); 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 * (trcent - tredge) + tredge; // transparency center ... edge if (kern < 0) kern = 0; // transp. 0 ... 100% >> kern 0 ... 1 if (kern > 1) kern = 1; if (rad > radius) kern = 2; // beyond radius, within square kernel[dx+radius][dy+radius] = kern; } } if (strmatch(event,"undlast")) // undo last edit (click or drag) pc_undo1(); if (strmatch(event,"undall")) { // undo all edits edit_reset(); pc_freeundo(); } if (strmatch(event,"gradual")) // flag, gradual overpaints zdialog_fetch(zd,"gradual",Fgradual); return 1; } // pixel paint mouse function void pclone_names::pc_mousefunc() { using namespace pclone_names; static int pmxdown = 0, pmydown = 0; int px, py; char color[20]; float *pix3; zdialog *zd = EFpaintclone.zd; if (LMclick && KBshiftkey) // shift + left mouse click { if (paint_mode == 1) { // paint color mode px = Mxclick; py = Myclick; pix3 = PXMpix(E3pxm,px,py); // 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); } if (paint_mode == 2) { // paint image mode 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 = Mxclick; py = Myclick; undoseq++; // new undo seq. no. draw_mousecircle(0,0,0,1); // erase mouse circle if (paint_mode == 2) draw_mousecircle2(0,0,0,1); // erase source tracking circle pc_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 undoseq++; // new undo seq. no. pmxdown = Mxdown; pmydown = Mydown; } draw_mousecircle(0,0,0,1); // erase mouse circle if (paint_mode == 2) draw_mousecircle2(0,0,0,1); // erase source tracking circle pc_dopixels(px,py); // do 1 block of pixels } draw_mousecircle(Mxposn,Myposn,Mradius,0); // draw mouse circle if (mode == 1 && paint_mode == 2 && (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); } else draw_mousecircle2(0,0,0,1); // no 2nd circle LMclick = RMclick = Mxdrag = Mydrag = 0; return; } // paint or erase 1 block of pixels within mouse radius of px, py void pclone_names::pc_dopixels(int px, int py) { using namespace pclone_names; float *pix1, *pix3, *pix9; int radius, dx, dy, qx, qy, sx, sy; int ii, ww, hh, dist = 0; float red, green, blue; float kern; if (paint_mode == 2 && ! imagex && ! imagey) return; // no source area defined ww = E3pxm->ww; hh = E3pxm->hh; pc_saveundo(px,py); // save pixels for poss. undo red = RGB[0]; // paint color green = RGB[1]; blue = RGB[2]; 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 transparencies if (kern > 1) continue; // outside mouse radius if (! Fgradual) kern = 0; // sudden paint or erase if (sa_stat == 3 && dist < sa_blend) // at select area edge, kern = kern + (1.0 - kern) * (sa_blend - dist) / sa_blend; // transparency = 100% pix1 = PXMpix(E1pxm,qx,qy); // source image pixel pix3 = PXMpix(E3pxm,qx,qy); // edited image pixel if (mode == 1 && paint_mode == 1) // paint with color { if (Fgradual) { pix3[0] = (1.0 - kern) * red + kern * pix3[0]; // overpaints accumulate pix3[1] = (1.0 - kern) * green + kern * pix3[1]; pix3[2] = (1.0 - kern) * blue + kern * pix3[2]; } else { // paint sudden pix3[0] = (1.0 - kern) * red + kern * pix1[0]; pix3[1] = (1.0 - kern) * green + kern * pix1[1]; pix3[2] = (1.0 - kern) * blue + kern * pix1[2]; } } if (mode == 1 && paint_mode == 2) // paint from other image location (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 if (Fgradual) { pix3[0] = (1.0 - kern) * pix9[0] + kern * pix3[0]; // overpaints accumulate pix3[1] = (1.0 - kern) * pix9[1] + kern * pix3[1]; pix3[2] = (1.0 - kern) * pix9[2] + kern * pix3[2]; } else { pix3[0] = (1.0 - kern) * pix9[0] + kern * pix1[0]; // paint sudden pix3[1] = (1.0 - kern) * pix9[1] + kern * pix1[1]; pix3[2] = (1.0 - kern) * pix9[2] + kern * pix1[2]; } } if (mode == 2) // unpaint or erase { if (Fgradual) { pix3[0] = (1.0 - kern) * pix1[0] + kern * pix3[0]; // gradual erase pix3[1] = (1.0 - kern) * pix1[1] + kern * pix3[1]; pix3[2] = (1.0 - kern) * pix1[2] + kern * pix3[2]; } else { pix3[0] = pix1[0]; // sudden erase pix3[1] = pix1[1]; pix3[2] = pix1[2]; } } } px = px - radius - 1; // repaint modified area py = py - radius - 1; ww = 2 * radius + 3; Fpaint3(px,py,ww,ww); return; } // save 1 block of pixels for possible undo void pclone_names::pc_saveundo(int px, int py) { using namespace pclone_names; int cc, npix, radius, dx, dy; float *pix3; savepix_t *pclonesave1; int mempercent; static int ppercent = 0; zdialog *zd = EFpaintclone.zd; if (! undopixmem) // first call { undopixmem = (savepix_t **) zmalloc(pc_undomaxpix * sizeof(void *)); undototpix = 0; undototmem = 0; } if (undototmem > pc_undomaxmen) { zmessageACK(Mwin,ZTX("Undo memory limit has been reached. \n" "Save work with [done], then resume painting.")); Mdrag = 0; return; } 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 * 3 * sizeof(float) + savepix_cc; pclonesave1 = (savepix_t *) zmalloc(cc); // allocate memory for block undopixmem[undototpix] = pclonesave1; undototpix += 1; undototmem += cc; // 15.08 pclonesave1->seq = undoseq; // save pixel block poop pclonesave1->px = px; pclonesave1->py = py; pclonesave1->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 pclonesave1->pixel[npix][0] = pix3[0]; pclonesave1->pixel[npix][1] = pix3[1]; pclonesave1->pixel[npix][2] = pix3[2]; npix++; } mempercent = int(100.0 * undototmem / pc_undomaxmen); // update undo memory status if (mempercent != ppercent) { ppercent = mempercent; snprintf(undomemmessage,99,undomemformat,mempercent,'%'); zdialog_stuff(zd,"labmem",undomemmessage); } return; } // undo last undo sequence number void pclone_names::pc_undo1() { using namespace pclone_names; int ii, cc, npix, radius, mempercent; int ww, px, py, dx, dy; float *pix3; zdialog *zd = EFpaintclone.zd; savepix_t *pclonesave1; for (ii = undototpix-1; ii >= 0; ii--) { pclonesave1 = undopixmem[ii]; if (pclonesave1->seq != undoseq) break; px = pclonesave1->px; py = pclonesave1->py; radius = pclonesave1->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] = pclonesave1->pixel[npix][0]; pix3[1] = pclonesave1->pixel[npix][1]; pix3[2] = pclonesave1->pixel[npix][2]; npix++; } px = px - radius - 1; py = py - radius - 1; ww = 2 * radius + 3; Fpaint3(px,py,ww,ww); zfree(pclonesave1); undopixmem[ii] = 0; cc = npix * 3 * sizeof(float) + savepix_cc; undototmem -= cc; undototpix--; } if (undoseq > 0) --undoseq; mempercent = int(100.0 * undototmem / pc_undomaxmen); // update undo memory status snprintf(undomemmessage,99,undomemformat,mempercent,'%'); zdialog_stuff(zd,"labmem",undomemmessage); return; } // free all undo memory void pclone_names::pc_freeundo() { using namespace pclone_names; int ii; savepix_t *pclonesave1; zdialog *zd = EFpaintclone.zd; for (ii = undototpix-1; ii >= 0; ii--) { pclonesave1 = undopixmem[ii]; zfree(pclonesave1); } if (undopixmem) zfree(undopixmem); undopixmem = 0; undoseq = 0; undototpix = 0; undototmem = 0; if (zd) { snprintf(undomemmessage,99,undomemformat,0,'%'); // undo memory = 0% zdialog_stuff(zd,"labmem",undomemmessage); } 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 Fgradual = 1; // gradual or sudden transparency int Mradius; // mouse radius float kernel[400][400]; // radius <= 199 editfunc EFpaintransp; } // menu function void m_paint_transp(GtkWidget *, cchar *) // 15.08 { 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,"spin","radius","vbbr2","1|199|1|30"); zdialog_add_widget(zd,"spin","stcent","vbbr2","0|100|1|95"); zdialog_add_widget(zd,"spin","stedge","vbbr2","0|100|1|100"); zdialog_add_widget(zd,"hbox","hb4","dialog",0,"space=3"); zdialog_add_widget(zd,"check","gradual","hb4",ZTX("gradual paint"),"space=5"); zdialog_stuff(zd,"gradual",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,"gradual",Fgradual); // 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,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel erase_topcircles(); if (zd->zstat) { if (zd->zstat == 1) { // commit edit edit_done(0); Fwarnalpha = 1; // warn if save to jpeg } 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,"gradual")) // flag, gradual overpaints zdialog_fetch(zd,"gradual",Fgradual); 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; draw_mousecircle(Mxposn,Myposn,Mradius,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; 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 * (1.0 - (sa_blend - dist) / sa_blend); // kern = kern ... 0 at edge pix3 = PXMpix(E3pxm,qx,qy); // edited image pixel if (mode == 1) { // add transparency if (Fgradual) { pix3[3] -= 2 * kern; // accumulate transparency if (pix3[3] < 0) pix3[3] = 0; } else pix3[3] = 0; // instant transparency } if (mode == 2) { // add opacity if (Fgradual) { 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 15.09 py = py - radius - 1; ww = 2 * radius + 3; Fpaint3(px,py,ww,ww); return; } /**************************************************************************/ // make a black & white or color positive or negative, or sepia image editfunc EFcolormode; // menu function void m_color_mode(GtkWidget *, cchar *menu) { int colormode_dialog_event(zdialog *zd, cchar *event); F1_help_topic = "color_mode"; EFcolormode.menuname = menu; EFcolormode.menufunc = m_color_mode; EFcolormode.funcname = "color_mode"; EFcolormode.Farea = 2; // select area usable EFcolormode.Frestart = 1; // allow restart EFcolormode.Fscript = 1; // scriptging supported 15.10 if (! edit_setup(EFcolormode)) return; // setup edit: no preview zdialog *zd = zdialog_new(ZTX("Color Mode"),Mwin,Bdone,Bcancel,null); EFcolormode.zd = zd; zdialog_add_widget(zd,"radio","none","dialog",ZTX("no change")); zdialog_add_widget(zd,"radio","b&wpos","dialog",ZTX("black/white positive")); zdialog_add_widget(zd,"radio","b&wneg","dialog",ZTX("black/white negative")); zdialog_add_widget(zd,"radio","colpos","dialog",ZTX("color positive")); zdialog_add_widget(zd,"radio","colneg","dialog",ZTX("color negative")); zdialog_add_widget(zd,"radio","sepia","dialog",ZTX("sepia")); 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) { int ii, dist, px, py; int color_mode; float red1, green1, blue1; float red3, green3, blue3; float *pix1, *pix3; float dnew, dold; dist = 0; // stop compiler warnings red3 = green3 = blue3 = 0; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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; zdialog_fetch(zd,"none",ii); if (ii) color_mode = 0; zdialog_fetch(zd,"b&wpos",ii); if (ii) color_mode = 1; zdialog_fetch(zd,"b&wneg",ii); if (ii) color_mode = 2; zdialog_fetch(zd,"colpos",ii); if (ii) color_mode = 3; zdialog_fetch(zd,"colneg",ii); if (ii) color_mode = 4; zdialog_fetch(zd,"sepia",ii); if (ii) color_mode = 5; for (py = 0; py < E3pxm->hh; py++) 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); red1 = pix1[0]; green1 = pix1[1]; blue1 = pix1[2]; switch (color_mode) { case 0: { edit_reset(); return 1; } case 1: { // black and white positive red3 = green3 = blue3 = (red1 + green1 + blue1) / 3.0; CEF->Fmods++; CEF->Fsaved = 0; break; } case 2: { // black and white negative red3 = green3 = blue3 = 255.9 - (red1 + green1 + blue1) / 3.0; CEF->Fmods++; CEF->Fsaved = 0; break; } case 3: { // color positive (no change) red3 = red1; green3 = green1; blue3 = blue1; CEF->Fmods = 0; CEF->Fsaved = 0; break; } case 4: { // color negative red3 = 255.9 - red1; green3 = 255.9 - green1; blue3 = 255.9 - blue1; CEF->Fmods++; CEF->Fsaved = 0; break; } case 5: { // sepia: RGB 112|66|20 red3 = green3 = blue3 = (red1 + green1 + blue1) / 3.0; green3 = 0.59 * red3; blue3 = 0.18 * red3; CEF->Fmods++; CEF->Fsaved = 0; break; } } if (red3 < 0) red3 = 0; // prevent underflow/overlfow 15.05 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; pix3 = PXMpix(E3pxm,px,py); if (sa_stat == 3 && dist < sa_blend) { // blend changes over blendwidth dnew = 1.0 * dist / sa_blend; 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; } Fpaint2(); return 1; } /**************************************************************************/ // 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 *, const char *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 15.10 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); // 15.01 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 15.05 zdialog_resize(zd,300,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,"enter")) zd->zstat = 2; // KB input if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 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 = 1.0 * dist / sa_blend; // 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 } /**************************************************************************/ // 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 *, const char *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 15.10 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); // 15.01 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,"enter")) zd->zstat = 2; // KB input if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 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 15.05 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 = 1.0 * dist / sa_blend; // 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 float H, S, L, R, G, B, C, A; float Rm, Gm, Bm; // color to match and change } void HSLtoRGB(float H, float S, float L, float &R, float &G, float &B); // menu function void m_adjust_HSL(GtkWidget *, const char *) { 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 /*** __________________________________________________ | | | Color to change [#####] | | [########] [################################] | // result color and hue spectrum | Color Hue ================[]================ | | Saturation ====================[]============ | | Lightness ===========[]===================== | | Color Match ================[]================ | | Adjustment ================[]================ | | 0% 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",0,"space=3"); zdialog_add_widget(zd,"label","labmatch","hb1",ZTX("Color to change"),"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","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,"frame","RGBframe","vb1"); // 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,"label","labhue","vb1",ZTX("Color Hue")); zdialog_add_widget(zd,"label","labsat","vb1",ZTX("Saturation")); zdialog_add_widget(zd,"label","lablite","vb1",ZTX("Lightness")); zdialog_add_widget(zd,"label","lablite","vb1",ZTX("Color Match")); zdialog_add_widget(zd,"label","labadj","vb1",ZTX("Adjustment")); zdialog_add_widget(zd,"label","space","vb1",0); zdialog_add_widget(zd,"frame","Hframe","vb2"); // 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,"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"); zdialog_add_widget(zd,"hscale","C","vb2","0|1|0.001|1.0","expand"); zdialog_add_widget(zd,"hscale","A","vb2","0|1|0.001|0.0","expand"); zdialog_add_widget(zd,"hbox","hbpct","vb2"); zdialog_add_widget(zd,"label","lab0%","hbpct","0%"); zdialog_add_widget(zd,"label","space","hbpct",0,"expand"); zdialog_add_widget(zd,"label","lab100%","hbpct","100%"); H = 180; // set dialog defaults S = 0.5; L = 0.5; C = 1.0; A = 0.0; Rm = Gm = Bm = 0; // color to change = black = not set zdialog_run(zd,HSL_dialog_event,"save"); // run dialog - parallel takeMouse(HSL_mousefunc,arrowcursor); // connect mouse function return; } // Paint RGBcolor drawing area with current RGB color void HSL_RGBcolor(GtkWidget *drawarea, cairo_t *cr, int *) { using namespace HSL_names; int ww, hh; HSLtoRGB(H,S,L,R,G,B); ww = gtk_widget_get_allocated_width(drawarea); // drawing area size hh = gtk_widget_get_allocated_height(drawarea); 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_Hscale(GtkWidget *drawarea, cairo_t *cr, int *) { using namespace HSL_names; int px, ww, hh; float H, R, G, B; ww = gtk_widget_get_allocated_width(drawarea); // drawing area size hh = gtk_widget_get_allocated_height(drawarea); 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_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,"enter")) zd->zstat = 2; // KB input if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 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 H = 180; // set defaults S = 0.5; L = 0.5; C = 1.0; A = 0.0; zdialog_stuff(zd,"H",H); zdialog_stuff(zd,"S",S); zdialog_stuff(zd,"L",L); zdialog_stuff(zd,"C",C); // 15.11 zdialog_stuff(zd,"A",A); 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("H",event)) { // color hue 0-360 zdialog_fetch(zd,"H",H); mod = 1; } if (strmatch("S",event)) { // saturation 0-1 zdialog_fetch(zd,"S",S); mod = 1; } if (strmatch("L",event)) { // lightness 0-1 zdialog_fetch(zd,"L",L); mod = 1; } if (strmatch("C",event)) { // color match 0-100% 15.11 zdialog_fetch(zd,"C",C); mod = 1; } if (strmatch("A",event)) { // adjustment 0-100% zdialog_fetch(zd,"A",A); mod = 1; } if (strmatch("blendwidth",event)) mod = 1; // area blend width changed if (mod) { HSLtoRGB(H,S,L,R,G,B); // update RGB color gtk_widget_queue_draw(RGBcolor); // draw current RGB color gtk_widget_queue_draw(Hscale); // draw HSL scale with new S and L signal_thread(); // trigger update thread } return 1; } // mouse function // click on image to set the color to match and change void HSL_mousefunc() // 15.11 { using namespace HSL_names; char color[20]; float *pix1; zdialog *zd = EFHSL.zd; if (LMclick && KBshiftkey) // shift + 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(color,19,"%.0f|%.0f|%.0f",Rm,Gm,Bm); if (zd) zdialog_stuff(zd,"matchRGB",color); } 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 Rx, Gx, Bx; float R1, G1, B1, R3, G3, B3; float a1, a2, f1, f2, match; Rx = R * 255; // RGB color to add to image Gx = G * 255; Bx = B * 255; 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 pixel RGB G1 = pix1[1]; B1 = pix1[2]; match = RGBMATCH(R1,G1,B1,Rm,Gm,Bm); // 1.0 = perfect match 15.11 a1 = pow(match, 10.0 * C); // color selectivity, min...max a1 = A * a1; a2 = 1.0 - a1; R3 = a1 * Rx + a2 * R1; // blend adjustment 0-100% G3 = a1 * Gx + a2 * G1; B3 = a1 * Bx + a2 * B1; if (sa_stat == 3 && dist < sa_blend) { // select area is active, f1 = 1.0 * dist / sa_blend; // 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; pix3[1] = G3; pix3[2] = 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 // each RGB = 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; } /**************************************************************************/ // ramp brightness across image, vertical and horizontal gradients editfunc EFbrramp; int brramp_spc; // current spline curves (2 at a time) void m_brightramp(GtkWidget *, cchar *menu) // upgrade for 4 colors { int brramp_dialog_event(zdialog* zd, cchar *event); void brramp_curvedit(int spc); void * brramp_thread(void *); void brramp_init(); F1_help_topic = "brightness_ramp"; EFbrramp.menuname = menu; EFbrramp.menufunc = m_brightramp; EFbrramp.funcname = "bright-ramp"; EFbrramp.FprevReq = 1; // use preview EFbrramp.Farea = 2; // select area usable EFbrramp.Fscript = 1; // scripting supported 15.10 EFbrramp.threadfunc = brramp_thread; if (! edit_setup(EFbrramp)) return; // setup edit /*** _________________________________________ | | | | | curve edit area | | | |_________________________________________| (o) all (o) red (o) green (o) blue // colors: all, red, green, blue Curve File: [Open] [Save] // each has vert. and horiz. curve [Done] [Cancel] ***/ zdialog *zd = zdialog_new(ZTX("Ramp brightness across image"),Mwin,Bdone,Bcancel,null); EFbrramp.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5|expand"); zdialog_add_widget(zd,"vbox","vb1","hb1"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"expand"); zdialog_add_widget(zd,"label","lmax","vb1","+","space=3"); zdialog_add_widget(zd,"label","lspace","vb1",0,"expand"); zdialog_add_widget(zd,"label","lmin","vb1","‒","space=3"); zdialog_add_widget(zd,"label","lspace","vb1"); zdialog_add_widget(zd,"frame","frame","vb2",0,"expand"); zdialog_add_widget(zd,"hbox","hb2","vb2"); zdialog_add_widget(zd,"label","lmin","hb2","‒","space=3"); zdialog_add_widget(zd,"label","lspace","hb2",0,"expand"); zdialog_add_widget(zd,"label","lmax","hb2","+","space=3"); zdialog_add_widget(zd,"hbox","hbrgb","dialog",0,"space=3"); zdialog_add_widget(zd,"radio","all","hbrgb",Ball,"space=5"); zdialog_add_widget(zd,"radio","red","hbrgb",Bred,"space=5"); zdialog_add_widget(zd,"radio","green","hbrgb",Bgreen,"space=5"); zdialog_add_widget(zd,"radio","blue","hbrgb",Bblue,"space=5"); zdialog_add_widget(zd,"hbox","hbcf","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labcf","hbcf",Bcurvefile,"space=8"); zdialog_add_widget(zd,"button","loadcurves","hbcf",Bopen,"space=5"); zdialog_add_widget(zd,"button","savecurves","hbcf",Bsave,"space=5"); GtkWidget *frame = zdialog_widget(zd,"frame"); spldat *sd = splcurve_init(frame,brramp_curvedit); EFbrramp.curves = sd; brramp_init(); // set up initial flat curves zdialog_stuff(zd,"all",1); // stuff default selection, all zdialog_resize(zd,260,350); zdialog_run(zd,brramp_dialog_event,"save"); // run dialog - parallel return; } // initialize all curves to flat void brramp_init() { spldat *sd = EFbrramp.curves; sd->Nspc = 8; // 8 curves brramp_spc = 0; // current curves: all (0-1) for (int ii = 0; ii < 8; ii++) // loop curves: all (0-1), { // red (2-3), green (4-5), blue (6-7) sd->fact[ii] = 0; // 15.10 sd->nap[ii] = 2; // horizontal curve ii sd->vert[ii] = 0; sd->apx[ii][0] = 0.01; sd->apx[ii][1] = 0.99; sd->apy[ii][0] = sd->apy[ii][1] = 0.5; splcurve_generate(sd,ii); ii++; sd->fact[ii] = 0; // 15.10 sd->nap[ii] = 2; // vertical curve ii+1 sd->vert[ii] = 1; sd->apx[ii][0] = 0.01; sd->apx[ii][1] = 0.99; sd->apy[ii][0] = sd->apy[ii][1] = 0.5; splcurve_generate(sd,ii); } sd->fact[0] = sd->fact[1] = 1; // 2 active curves 15.10 return; } // dialog event and completion callback function int brramp_dialog_event(zdialog *zd, cchar *event) { spldat *sd = EFbrramp.curves; int ii, nn, Fupdate = 0; if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"enter")) zd->zstat = 1; if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"apply")) Fupdate++; // from script 15.10 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,"blendwidth")) signal_thread(); if (strstr("all red green blue",event)) { // new choice of curve zdialog_fetch(zd,event,ii); if (! ii) return 0; // ignore button OFF events for (ii = 0; ii < 8; ii++) // set active curves 15.10 sd->fact[ii] = 0; ii = strmatchV(event,"all","red","green","blue",null); // 1-4 brramp_spc = ii = 2 * (ii-1); // 0/1, 2/3, 4/5, 6/7 sd->fact[ii] = sd->fact[ii+1] = 1; Fupdate++; } if (strmatch(event,"loadcurves")) // load 8 saved curves from file { nn = splcurve_load(sd); if (nn != 1) return 0; Fupdate++; } if (strmatch(event,"savecurves")) // save 8 curves to file splcurve_save(sd); if (Fupdate) // curve has changed { gtk_widget_queue_draw(sd->drawarea); // draw curve signal_thread(); // trigger image update } return 1; } // this function is called when a curve is edited void brramp_curvedit(int spc) { int ii = brramp_spc, jj; spldat *sd = EFbrramp.curves; // active curves: 2, 4, 6, 8 (and +1) if (brramp_spc == 0) { // "all" curve was edited for (ii = 2; ii < 8; ii += 2) { // fix R/G/B curves to match sd->nap[ii] = sd->nap[0]; for (jj = 0; jj < sd->nap[0]; jj++) { sd->apx[ii][jj] = sd->apx[0][jj]; sd->apy[ii][jj] = sd->apy[0][jj]; } for (jj = 0; jj < 1000; jj++) sd->yval[ii][jj] = sd->yval[0][jj]; } for (ii = 3; ii < 8; ii += 2) { sd->nap[ii] = sd->nap[1]; for (jj = 0; jj < sd->nap[1]; jj++) { sd->apx[ii][jj] = sd->apx[1][jj]; sd->apy[ii][jj] = sd->apy[1][jj]; } for (jj = 0; jj < 1000; jj++) sd->yval[ii][jj] = sd->yval[1][jj]; } } signal_thread(); return; } // brramp thread function void * brramp_thread(void *arg) { void * brramp_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(brramp_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 * brramp_wthread(void *arg) // worker thread function { int index = *((int *) arg); int ii, jj, kk, dist = 0, px3, py3; float *pix1, *pix3; float red1, green1, blue1, maxrgb; float red3, green3, blue3; float dispx, dispy; float hramp[3], vramp[3], tramp[3]; float spanw, spanh, dold, dnew; spldat *sd = EFbrramp.curves; if (sa_stat == 3) { // if select area active, ramp spanw = sa_maxx - sa_minx; // brightness over enclosing rectangle spanh = sa_maxy - sa_miny; } else { spanw = E3pxm->ww; // else over entire image spanh = E3pxm->hh; } for (py3 = index; py3 < E3pxm->hh; py3 += NWT) // loop output pixels for (px3 = 0; px3 < E3pxm->ww; px3++) { if (sa_stat == 3) { // select area active ii = py3 * E3pxm->ww + px3; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area dispx = (px3 - sa_minx) / spanw; dispy = (py3 - sa_miny) / spanh; } else { dispx = px3 / spanw; // left > right = 0 to 1 dispy = py3 / spanh; // top > bottom = 0 to 1 } kk = 1000 * dispx; // conv. dispx 0-1 to 0-1000 if (kk > 999) kk = 999; for (ii = 0, jj = 2; ii < 3; ii++, jj += 2) // horz. curves for dispx and R/G/B hramp[ii] = sd->yval[jj][kk] - 0.5; // scale to -0.5 to +0.5 kk = 1000 * dispy; // conv. dispy if (kk > 999) kk = 999; for (ii = 0, jj = 3; ii < 3; ii++, jj += 2) // vert. curves vramp[ii] = sd->yval[jj][kk] - 0.5; for (ii = 0; ii < 3; ii++) // total R/G/B change tramp[ii] = 1.0 + hramp[ii] + vramp[ii]; // scale to 0.0 to 2.0 pix1 = PXMpix(E1pxm,px3,py3); // input pixel red1 = pix1[0]; green1 = pix1[1]; blue1 = pix1[2]; red3 = red1 * tramp[0]; // change R/G/B green3 = green1 * tramp[1]; blue3 = blue1 * tramp[2]; maxrgb = red3; if (green3 > maxrgb) maxrgb = green3; if (blue3 > maxrgb) maxrgb = blue3; if (maxrgb > 255.9) { // stop overflow 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 = 1.0 * dist / sa_blend; 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 } /**************************************************************************/ // Color ramp function. 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 color_ramp_dialog(); void color_ramp_mousefunc(); void color_ramp_stuff(); void * color_ramp_thread(void *); int color_ramp_pixel[9][2]; // last 9 pixel locations clicked float color_ramp_val1[9][3]; // original RGB values, 0 to 255.9 float color_ramp_val3[9][3]; // revised RGB values, 0 to 255.9 int color_ramp_metric = 1; // 1/2/3 = /RGB/EV/OD int color_ramp_delta = 0; // flag, delta mode int color_ramp_npix; // no. of selected pixels double color_ramp_time; // last click time int color_ramp_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_color_ramp(GtkWidget *, cchar *menu) { int color_ramp_event(zdialog *zd, cchar *event); cchar *limits; zdialog *zd; cchar *mess = ZTX("Click image to select pixels."); F1_help_topic = "color_ramp"; EFrgbrevise.menufunc = m_color_ramp; EFrgbrevise.funcname = "color_ramp"; // setup edit function EFrgbrevise.threadfunc = color_ramp_thread; EFrgbrevise.mousefunc = color_ramp_mousefunc; EFrgbrevise.Farea = 1; if (! edit_setup(EFrgbrevise)) return; /*** Click image to select pixels. [x] delta Metric: (o) RGB (o) EV (o) OD Pixel Red Green Blue A xxxx xxxx [xxxx|v] [xxxx|v] [xxxx|v] // spin buttons for RGB/EV/OD values B xxxx xxxx [xxxx|v] [xxxx|v] [xxxx|v] C xxxx xxxx [xxxx|v] [xxxx|v] [xxxx|v] D xxxx xxxx [xxxx|v] [xxxx|v] [xxxx|v] E xxxx xxxx [xxxx|v] [xxxx|v] [xxxx|v] F xxxx xxxx [xxxx|v] [xxxx|v] [xxxx|v] G xxxx xxxx [xxxx|v] [xxxx|v] [xxxx|v] H xxxx xxxx [xxxx|v] [xxxx|v] [xxxx|v] I xxxx xxxx [xxxx|v] [xxxx|v] [xxxx|v] Blend ==============[]================ [reset] [done] [cancel] ***/ zd = zdialog_new(ZTX("Color Ramp"),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",color_ramp_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 (color_ramp_metric == 1) zdialog_stuff(zd,"radRGB",1); // set current metric if (color_ramp_metric == 2) zdialog_stuff(zd,"radEV",1); if (color_ramp_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 (color_ramp_metric == 1) limits = "-255.9|255.9|0.1|0.0"; // metric = RGB else if (color_ramp_metric == 2) limits = "-8|8|0.01|0.0"; // EV else limits = "-3|3|0.002|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,"spin",redx,hbdatx,limits,"space=3"); // add spin buttons for "red1" to "red9" etc. zdialog_add_widget(zd,"spin",greenx,hbdatx,limits,"space=3"); zdialog_add_widget(zd,"spin",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 color_ramp_stuff(); else { color_ramp_blend = 20; // default slider value color_ramp_npix = 0; // no pixels selected yet } zdialog_run(zd,color_ramp_event,"save"); // run dialog takeMouse(color_ramp_mousefunc,dragcursor); // connect mouse function return; } // dialog event function int color_ramp_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,"enter")) zd->zstat = 2; // KB input if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 if (zd->zstat) { if (zd->zstat == 1) { // Reset zd->zstat = 0; // keep dialog active color_ramp_npix = 0; // no pixels selected yet color_ramp_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(color_ramp_mousefunc,dragcursor); // connect mouse function if (strmatch(event,"blend")) { // new blend factor zdialog_fetch(zd,"blend",color_ramp_blend); snprintf(text,8,"%d",color_ramp_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) color_ramp_metric = 1; } if (strmatch(event,"radEV")) { // EV zdialog_fetch(zd,event,button); if (button) color_ramp_metric = 2; } if (strmatch(event,"radOD")) { // OD zdialog_fetch(zd,event,button); if (button) color_ramp_metric = 3; } if (button) { // restart with new limits edit_cancel(0); m_color_ramp(0,"restart"); } return 1; } if (strmatch(event,"delta")) { // change absolute/delta mode zdialog_fetch(zd,"delta",color_ramp_delta); edit_cancel(0); m_color_ramp(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 = color_ramp_val1[ii][jj]; // original pixel RGB value if (color_ramp_metric == 2) val1 = RGB2EV(val1); // convert to EV or OD units if (color_ramp_metric == 3) val1 = RGB2OD(val1); zdialog_fetch(zd,event,val3); // revised RGB/EV/OD value from dialog if (color_ramp_delta) val3 += val1; // if delta mode, make absolute if (color_ramp_metric == 2) val3 = EV2RGB(val3); // convert EV/OD to RGB value if (color_ramp_metric == 3) val3 = OD2RGB(val3); if (fabsf(color_ramp_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; color_ramp_val3[ii][jj] = val3; // new RGB value for pixel if (color_ramp_metric == 2) val3 = RGB2EV(val3); // convert RGB to EV/OD units if (color_ramp_metric == 3) val3 = RGB2OD(val3); if (color_ramp_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 color_ramp_mousefunc() // mouse function { int ii; float *pix3; if (! LMclick) return; LMclick = 0; color_ramp_time = get_seconds(); // mark time of pixel click if (color_ramp_npix == 9) { // if table is full (9 entries) for (ii = 1; ii < 9; ii++) { // move positions 1-8 up color_ramp_pixel[ii-1][0] = color_ramp_pixel[ii][0]; // to fill positions 0-7 color_ramp_pixel[ii-1][1] = color_ramp_pixel[ii][1]; } color_ramp_npix = 8; // count is now 8 entries } ii = color_ramp_npix; // next table position to fill color_ramp_pixel[ii][0] = Mxclick; // newest pixel color_ramp_pixel[ii][1] = Myclick; pix3 = PXMpix(E3pxm,Mxclick,Myclick); // get initial RGB values from color_ramp_val1[ii][0] = color_ramp_val3[ii][0] = pix3[0]; // modified image E3 color_ramp_val1[ii][1] = color_ramp_val3[ii][1] = pix3[1]; color_ramp_val1[ii][2] = color_ramp_val3[ii][2] = pix3[2]; color_ramp_npix++; // up pixel count color_ramp_stuff(); // stuff pixels and values into dialog return; } // stuff dialog with current pixels and their RGB/EV/OD values void color_ramp_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 = color_ramp_pixel[ii][0]; // next pixel to report py = color_ramp_pixel[ii][1]; if (ii >= color_ramp_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 = color_ramp_val1[ii][0]; // original RGB values for pixel green1 = color_ramp_val1[ii][1]; blue1 = color_ramp_val1[ii][2]; red3 = color_ramp_val3[ii][0]; // revised RGB values green3 = color_ramp_val3[ii][1]; blue3 = color_ramp_val3[ii][2]; if (color_ramp_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 (color_ramp_metric == 3) { // or OD units red1 = RGB2OD(red1); green1 = RGB2OD(green1); blue1 = RGB2OD(blue1); red3 = RGB2OD(red3); green3 = RGB2OD(green3); blue3 = RGB2OD(blue3); } if (color_ramp_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",color_ramp_blend); signal_thread(); return; } // thread function - multiple working threads to update image void * color_ramp_thread(void *) { void * color_ramp_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 (color_ramp_npix < 1) continue; // must have 1+ pixels in table for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(color_ramp_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 * color_ramp_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 * color_ramp_blend * color_ramp_blend * blend + 100; // 100 to 2 * E1pxm->ww**2 for (ii = 0; ii < 9; ii++) // stop misguided gcc warning dist[ii] = 1; for (py1 = index; py1 < E3pxm->hh; py1 += NWT) // loop all image pixels for (px1 = 0; px1 < E3pxm->ww; px1++) { for (sumdist = ii = 0; ii < color_ramp_npix; ii++) // compute weight of each revision point { px2 = color_ramp_pixel[ii][0]; py2 = color_ramp_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 < color_ramp_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 < color_ramp_npix; ii++) { // apply weighted color changes delta = color_ramp_val3[ii][0] - color_ramp_val1[ii][0]; // to each color red += weight[ii] * delta; delta = color_ramp_val3[ii][1] - color_ramp_val1[ii][1]; green += weight[ii] * delta; delta = color_ramp_val3[ii][2] - color_ramp_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,"spin","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); // 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,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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 15.09 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); // 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 } /**************************************************************************/ // convert color profile of current image editfunc EFcolorprof; char colorprof1[200] = "/usr/share/color/icc/colord/sRGB.icc"; char colorprof2[200] = "/usr/share/color/icc/colord/AdobeRGB1998.icc"; // menu function void m_color_profile(GtkWidget *, cchar *menu) { int colorprof_dialog_event(zdialog *zd, cchar *event); zdialog *zd; F1_help_topic = "color_profile"; EFcolorprof.menufunc = m_color_profile; EFcolorprof.funcname = "color_profile"; EFcolorprof.Frestart = 1; // allow restart if (! edit_setup(EFcolorprof)) return; // setup edit 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,"entry","prof1","hb1",0,"expand|size=50"); 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,"entry","prof2","hb2",0,"expand|size=50"); 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 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,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 2; // KB input if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 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) edit_done(0); // commit edit else edit_cancel(0); // discard edit 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 cmsxform = cmsCreateTransform(cmsprof1,TYPE_RGB_FLT,cmsprof2,TYPE_RGB_FLT,INTENT_PERCEPTUAL,0); if (! cmsxform) { zmessageACK(Mwin,"cmsCreateTransform() failed"); return 1; } fpix1 = E0pxm->pixels; // input and output pixels fpix2 = E3pxm->pixels; Npix = E0pxm->ww * E0pxm->hh; Ffuncbusy = 1; for (uint ii = 0; ii < 3 * Npix; ii++) // rescale to range 0 - 0.9999 fpix2[ii] = f256 * fpix1[ii]; while (Npix) { nn = Npix; if (nn > 100000) nn = 100000; cmsDoTransform(cmsxform,fpix2,fpix2,nn); // speed: 3 megapixels/sec for 3.3 GHz CPU fpix2 += nn * 3; Npix -= nn; zmainloop(); } 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] >= 256.0) fpix2[ii] = 255.99; } Ffuncbusy = 0; cmsDeleteTransform(cmsxform); // free resources cmsCloseProfile(cmsprof1); cmsCloseProfile(cmsprof2); CEF->Fmods++; // image is modified CEF->Fsaved = 0; Fpaint2(); // update window image return 1; } /**************************************************************************/ // 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,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (zd->zstat) { if (zd->zstat == 1) { // done wrapup_thread(8); // thread finish, exit if (! resource_lock(Fpaintlock)) return 1; // 15.02 PXM_free(E3pxm); E3pxm = E9pxm; // image 3 = image 9 E9pxm = 0; resource_unlock(Fpaintlock); 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")) { if (! resource_lock(Fpaintlock)) return 1; // 15.02 PXM_free(E3pxm); E3pxm = PXM_copy(E9pxm); resource_unlock(Fpaintlock); 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 while (Fpaintlock) zsleep(0.01); // prevent window blinking 15.02 if (! resource_lock(Fpaintlock)) continue; // 15.02 PXM_free(E3pxm); E3pxm = PXM_copy(E9pxm); resource_unlock(Fpaintlock); 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); // 15.09 if (! resource_lock(Fpaintlock)) return; // 15.02 Ffuncbusy = 1; PXM_free(E3pxm); E3pxm = PXM_copy(E9pxm); resource_unlock(Fpaintlock); 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 = 1.0 * dist / sa_blend; // changes over distance sa_blend 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,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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 target 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; if (! resource_lock(Fpaintlock)) return; // 15.02 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); // replace E3 E3pxm = E9pxm; E9pxm = 0; resource_unlock(Fpaintlock); if (sa_stat) sa_unselect(); // area is no longer valid CEF->Fmods = 1; // image modified CEF->Fsaved = 0; Fpaint2(); // update window return; } /**************************************************************************/ // 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,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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; } /**************************************************************************/ // 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[50]; int maxstuck = 50, 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] [done] [cancel] | |________________________________________________| ***/ zd = zdialog_new(ZTX("Stuck Pixels"),Mwin,Bopen,Bsave,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_zuserdir()); } 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,"enter")) zd->zstat = 3; // KB input if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open() if (strmatch(event,"escape")) zd->zstat = 4; // escape = cancel 15.07 if (zd->zstat) { if (zd->zstat == 1) { // open file zd->zstat = 0; // keep dialog active stuckpix_open(); } else if (zd->zstat == 2) { // save file zd->zstat = 0; // keep dialog active stuckpix_save(); } else if (zd->zstat == 3) { // 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,"entry","file","hb1",0,"expand|size=40"); zdialog_add_widget(zd,"button","browse","hb1",Bbrowse,"space=5"); zdialog_stuff(zd,"file",stuckpix_file); zdialog_run(zd,stuckpix_open_dialog_event); 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,"escape")) zd->zstat = 2; // escape = cancel 15.07 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,ZTX("file not found")); 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); // 15.09 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); // 15.09 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; } fotoxx-15.11.1/f.edit.cc0000664000175000017500000103650312616075370013350 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** Fotoxx image edit - Edit menu functions m_trimrotate trim/crop and rotate combination m_upright upright a rotated image m_voodoo1 automatic image enhancement with limited smarts m_voodoo2 automatic image enhancement with limited smarts m_combo adjust brightness, contrast, color, saturation, white balance m_britedist flatten and/or expand brightness distribution m_zonal_flatten flatten zonal brightness distribution m_tonemap apply adjustable tone mapping to an image m_resize resize (rescale) image m_flip horizontal or vertical mirror image m_write_text write text on an image gentext create text graphic with attributes and transparency m_write_line write lines or arrows on an image genline create line/arrow graphic with attributes and transparency m_paint_edits paint some edit function gradually with the mouse m_lever_edits apply some 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 // in fotoxx.h // int trimrect[4]; // int trimsize[2]; namespace trimrotate { int ptrimrect[4]; // prior trim rectangle (this image) int ptrimsize[2]; // prior trim size (previous image) float trimR; // trim ratio, width/height float rotate_goal, rotate_angle; // target and actual rotation int rotatecrop1 = 0, rotatecrop2; // default rotate with no autocrop int E0ww, E0hh, E3ww, E3hh; // full size image and preview size float FPR; // full size / preview ratio int Fguidelines, guidelineX, guidelineY; // horz/vert guidelines for rotate int Fcorner, KBcorner, Frotate; int Fmax = 0; editfunc EFtrimrotate; void dialog(); int dialog_event(zdialog *zd, cchar *event); void trim_customize(); int trim_customize_dialog_event(zdialog *zd, cchar *event); void mousefunc(); void KBfunc(int key); void trim_limits(); void trim_darkmargins(); void trim_final(); void rotate_func(); void trim_fullsize(); void drawlines(); void autotrim(); } // menu function void m_trimrotate(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_trimrotate; // menu function EFtrimrotate.funcname = "trim_rotate"; EFtrimrotate.FprevReq = 1; // use preview EFtrimrotate.Frestart = 1; // allow restart EFtrimrotate.mousefunc = mousefunc; if (! edit_setup(EFtrimrotate)) return; // setup edit PXM_addalpha(E0pxm); // 15.09 PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); E0ww = E0pxm->ww; // full-size image dimensions E0hh = E0pxm->hh; E3ww = E3pxm->ww; // preview image (possibly smaller) E3hh = E3pxm->hh; FPR = 1.0 * (E0ww + E0hh) / (E3ww + E3hh); // full image / preview ratio if (menu && strmatch(menu,"keep")) { // keep preset trim rectangle trimsize[0] = trimrect[2] - trimrect[0]; // (full image scale) trimsize[1] = trimrect[3] - trimrect[1]; trimR = 1.0 * trimsize[0] / trimsize[1]; // trim ratio = width/height } else { trimsize[0] = 0.8 * E0ww; // initial trim rectangle, 80% image size trimsize[1] = 0.8 * E0hh; xmargin = 0.5 * (E0ww - trimsize[0]); // set balanced margins ymargin = 0.5 * (E0hh - trimsize[1]); trimrect[0] = xmargin; trimrect[2] = E0ww - xmargin; trimrect[1] = ymargin; trimrect[3] = E0hh - ymargin; trimR = 1.0 * trimsize[0] / trimsize[1]; // trim ratio = width/height } trimrect[0] = trimrect[0] / FPR; // use preview scale from now on trimrect[1] = trimrect[1] / FPR; trimrect[2] = trimrect[2] / FPR; trimrect[3] = trimrect[3] / FPR; ptrimrect[0] = 0; // set prior trim rectangle ptrimrect[1] = 0; // = 100% of image ptrimrect[2] = E3ww; ptrimrect[3] = E3hh; rotate_goal = rotate_angle = 0; // initially no rotation rotatecrop2 = rotatecrop1; // rotate autocrop setting /*** ______________________________________________________________ | | | 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 | | Rotate: degrees [____|-+] [x] auto-trim | | | | 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"); zdialog_add_widget(zd,"label","labW","hb1",Bwidth,"space=5"); zdialog_add_widget(zd,"spin","width","hb1","20|20000|1|1000"); zdialog_add_widget(zd,"label","space","hb1",0,"space=5"); zdialog_add_widget(zd,"label","labH","hb1",Bheight,"space=5"); zdialog_add_widget(zd,"spin","height","hb1","20|20000|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"); 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_widget(zd,"hbox","hb3","dialog"); // ratio buttons inserted here 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"); // and [custom] button zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbrotmess","dialog"); zdialog_add_widget(zd,"label","labrotmess","hbrotmess",rotate_mess,"space=5"); zdialog_add_widget(zd,"hbox","hb5","dialog"); zdialog_add_widget(zd,"label","labrotate","hb5",ZTX("Rotate: degrees"),"space=5"); zdialog_add_widget(zd,"spin","degrees","hb5","-360|360|0.1|0"); zdialog_add_widget(zd,"check","rotatecrop","hb5",ZTX("auto-trim"),"space=20"); zdialog_add_widget(zd,"hbox","hb90","dialog"); 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",ptrimsize[0]); // preserve prior trim size zdialog_fetch(zd,"height",ptrimsize[1]); // for use by [prev] button zdialog_stuff(zd,"width",trimsize[0]); // stuff width, height, ratio as zdialog_stuff(zd,"height",trimsize[1]); // pre-calculated for this image snprintf(text,20,"%.2f ",trimR); zdialog_stuff(zd,"ratio",text); zdialog_stuff(zd,"degrees",0); zdialog_fetch(zd,"rotatecrop",rotatecrop1); // get curr. autocrop setting takeMouse(mousefunc,dragcursor); // connect mouse function currgrid = 1; // use trim/rotate grid PXM_free(E9pxm); E9pxm = PXM_copy(E3pxm); Fzoom = 0; trim_darkmargins(); zdialog_run(zd,dialog_event,"save"); // run dialog - parallel 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; if (strmatch(event,"done")) zd->zstat = 3; // apply and quit if (strmatch(event,"enter")) zd->zstat = 3; if (strmatch(event,"cancel")) zd->zstat = 4; // cancel if (strmatch(event,"escape")) zd->zstat = 4; // escape = cancel 15.07 if (zd->zstat) // dialog complete { if (zd->zstat == 1) { // [grid] zd->zstat = 0; // keep dialog active m_gridlines(0,"grid 1"); return 1; } if (zd->zstat == 2) { // [apply] trim_fullsize(); CEF->Fmods++; edit_done(0); m_trimrotate(0,0); // restart edit return 1; } if (zd->zstat == 3) { // [done] trim_fullsize(); CEF->Fmods++; currgrid = 0; // restore normal grid settings 15.09 edit_done(0); return 1; } draw_toplines(2); // cancel - erase trim rectangle erase_topcircles(); // and target circle currgrid = 0; // restore normal grid settings edit_cancel(0); return 1; } if (strmatch(event,"custom")) { // [customize] button draw_toplines(2); // erase trim rectangle erase_topcircles(); // and target circle edit_cancel(0); // cancel edit trim_customize(); // customize dialog m_trimrotate(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 = ptrimsize[0]; height = ptrimsize[1]; 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); if (width < trimsize[0]) // increase full scale change as needed while (int(width/FPR) == int(trimsize[0]/FPR)) width--; // to make a change at preview scale if (width > trimsize[0]) while (int(width/FPR) == int(trimsize[0]/FPR)) width++; if (height < trimsize[1]) while (int(height/FPR) == int(trimsize[1]/FPR)) height--; if (height > trimsize[1]) while (int(height/FPR) == int(trimsize[1]/FPR)) height++; zdialog_fetch(zd,"lock",rlock); // lock ratio on/off width = width / FPR; // preview scale height = height / FPR; 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 - int(trimsize[0]/FPR); if (delta > 0) { // increased width trimrect[0] = trimrect[0] - delta / 2; // left and right sides equally trimrect[2] = trimrect[2] + delta / 2; if (delta % 2) { // if increase is odd trimrect[0] = trimrect[0] - flip; // add 1 alternatively to each side trimrect[2] = trimrect[2] + 1 - flip; } if (trimrect[0] < 0) { // add balance to upper limit trimrect[2] = trimrect[2] - trimrect[0]; trimrect[0] = 0; } if (trimrect[2] > E3ww) { // add balance to lower limit trimrect[0] = trimrect[0] - trimrect[2] + E3ww; trimrect[2] = E3ww; } } if (delta < 0) { // decreased width trimrect[0] = trimrect[0] - delta / 2; trimrect[2] = trimrect[2] + delta / 2; if (delta % 2) { trimrect[0] = trimrect[0] + flip; trimrect[2] = trimrect[2] - 1 + flip; } } delta = height - int(trimsize[1]/FPR); if (delta > 0) { // increased height trimrect[1] = trimrect[1] - delta / 2; // top and bottom sides equally trimrect[3] = trimrect[3] + delta / 2; if (delta % 2) { // if increase is odd trimrect[1] = trimrect[1] - flip; // add 1 alternatively to each side trimrect[3] = trimrect[3] + 1 - flip; } if (trimrect[1] < 0) { trimrect[3] = trimrect[3] - trimrect[1]; trimrect[1] = 0; } if (trimrect[3] > E3hh) { trimrect[1] = trimrect[1] - trimrect[3] + E3hh; trimrect[3] = E3hh; } } if (delta < 0) { // decreased height trimrect[1] = trimrect[1] - delta / 2; trimrect[3] = trimrect[3] + delta / 2; if (delta % 2) { trimrect[1] = trimrect[1] + flip; trimrect[3] = trimrect[3] - 1 + flip; } } if (trimrect[0] < 0) trimrect[0] = 0; // keep within limits if (trimrect[2] > E3ww) trimrect[2] = E3ww; // use ww not ww-1 if (trimrect[1] < 0) trimrect[1] = 0; if (trimrect[3] > E3hh) trimrect[3] = E3hh; width = trimrect[2] - trimrect[0]; // new width and height height = trimrect[3] - trimrect[1]; if (width > E3ww) width = E3ww; // limit to actual size if (height > E3hh) height = E3hh; width = width * FPR; // full image scale height = height * FPR; trimsize[0] = width; // new trim size trimsize[1] = 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 * trimsize[0] / trimsize[1]; snprintf(text,20,"%.2f ",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 { trimrect[0] = 0; trimrect[1] = 0; trimrect[2] = E3ww; trimrect[3] = E3hh; width = E3ww * FPR; // full scale trim size height = E3hh * FPR; trimsize[0] = width; // new trim size trimsize[1] = height; zdialog_stuff(zd,"width",width); // update dialog values zdialog_stuff(zd,"height",height); trimR = 1.0 * trimsize[0] / trimsize[1]; snprintf(text,20,"%.2f ",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 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 (trimrect[2] - trimrect[0] > trimrect[3] - trimrect[1]) trimrect[3] = trimrect[1] + (trimrect[2] - trimrect[0]) / trimR; // adjust smaller dimension else trimrect[2] = trimrect[0] + (trimrect[3] - trimrect[1]) * trimR; if (trimrect[2] > E3ww) { // if off the right edge, trimrect[2] = E3ww; // adjust height trimrect[3] = trimrect[1] + (trimrect[2] - trimrect[0]) / trimR; } if (trimrect[3] > E3hh) { // if off the bottom edge, trimrect[3] = E3hh; // adjust width trimrect[2] = trimrect[0] + (trimrect[3] - trimrect[1]) * trimR; } trimsize[0] = FPR * (trimrect[2] - trimrect[0]); // new rectangle dimensions trimsize[1] = FPR * (trimrect[3] - trimrect[1]); zdialog_stuff(zd,"width",trimsize[0]); // stuff width, height, ratio zdialog_stuff(zd,"height",trimsize[1]); snprintf(text,20,"%.2f ",trimR); zdialog_stuff(zd,"ratio",text); trim_darkmargins(); // update trim area in image return 1; } if (strmatch(event,"auto")) // 15.09 { if (CEF->Fmods) { dialog_event(zd,"max"); trim_fullsize(); edit_done(0); // complete edit } else edit_cancel(0); autotrim(); m_trimrotate(0,"keep"); // restart edit return 1; } 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 (strstr("+90 -90 180",event)) // assume no trim dialog_event(zd,"max"); } if (strmatch(event,"rotatecrop")) { zdialog_fetch(zd,"rotatecrop",rotatecrop1); // new autocrop setting rotate_func(); // E3 is rotated E1 } return 1; } // 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; 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,"entry",blab,"hbb",trimbuttons[ii],"size=6"); zdialog_add_widget(zd,"entry",rlab,"hbr",trimratios[ii],"size=6"); } zdialog_run(zd,trim_customize_dialog_event,"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; } // dialog event and completion function int trimrotate::trim_customize_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 return 1; } // trim/rotate mouse function void trimrotate::mousefunc() { using namespace trimrotate; int mpx, mpy, xdrag, ydrag; int moveall = 0; int dx, dy, dd, d1, d2, d3, d4; zdialog *zd = EFtrimrotate.zd; if (! LMclick && ! RMclick && ! Mxdrag && ! Mydrag) { // no click or drag Fcorner = Frotate = 0; return; } if (LMclick) // add vertical and horizontal { // lines at click position LMclick = 0; Fcorner = Frotate = 0; Fguidelines = 1; guidelineX = Mxclick; guidelineY = Myclick; trim_darkmargins(); Fpaint2(); return; } if (RMclick) // remove the lines { RMclick = 0; Fcorner = 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; } if (Frotate) goto mouse_rotate2; // continue rotate in progress else if (Fcorner) { if (Fcorner == 1) { trimrect[0] = mpx; trimrect[1] = mpy; } // continue dragging corner with mouse if (Fcorner == 2) { trimrect[2] = mpx; trimrect[1] = mpy; } if (Fcorner == 3) { trimrect[2] = mpx; trimrect[3] = mpy; } if (Fcorner == 4) { trimrect[0] = mpx; trimrect[3] = mpy; } } else { moveall = 1; dd = 0.2 * (trimrect[2] - trimrect[0]); // test if mouse is in the broad if (mpx < trimrect[0] + dd) moveall = 0; // middle of the rectangle if (mpx > trimrect[2] - dd) moveall = 0; dd = 0.2 * (trimrect[3] - trimrect[1]); if (mpy < trimrect[1] + dd) moveall = 0; if (mpy > trimrect[3] - dd) moveall = 0; } if (moveall) { // yes, move the whole rectangle trimrect[0] += xdrag; trimrect[2] += xdrag; trimrect[1] += ydrag; trimrect[3] += ydrag; } else if (! Fcorner) // no, find closest corner to mouse { dx = mpx - trimrect[0]; dy = mpy - trimrect[1]; d1 = sqrt(dx*dx + dy*dy); // distance from NW corner dx = mpx - trimrect[2]; dy = mpy - trimrect[1]; d2 = sqrt(dx*dx + dy*dy); // NE dx = mpx - trimrect[2]; dy = mpy - trimrect[3]; d3 = sqrt(dx*dx + dy*dy); // SE dx = mpx - trimrect[0]; dy = mpy - trimrect[3]; d4 = sqrt(dx*dx + dy*dy); // SW Fcorner = 1; // NW dd = d1; if (d2 < dd) { Fcorner = 2; dd = d2; } // NE if (d3 < dd) { Fcorner = 3; dd = d3; } // SE if (d4 < dd) { Fcorner = 4; dd = d4; } // SW if (E3ww - mpx < dd && mpx > 0.9 * E3ww) // mouse closer to right edge than corner if (mpy > 0.3 * E3hh && mpy < 0.7 * E3hh) // if far from corners assume rotate goto mouse_rotate1; if (Fcorner == 1) { trimrect[0] = mpx; trimrect[1] = mpy; } // move this corner to mouse if (Fcorner == 2) { trimrect[2] = mpx; trimrect[1] = mpy; } if (Fcorner == 3) { trimrect[2] = mpx; trimrect[3] = mpy; } if (Fcorner == 4) { trimrect[0] = mpx; trimrect[3] = mpy; } } KBcorner = Fcorner; // remember last corner moved trim_limits(); // check margin limits and adjust if req. trim_darkmargins(); // show trim area in image return; // ------------------------------------------------------------------------ mouse_rotate1: zdialog_send_event(zd,"max"); // v.15.11 mouse_rotate2: Frotate = 1; Fcorner = 0; rotate_goal += 30.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); rotate_func(); // E3 is rotated E1 return; } // Keyboard function // KB arrow keys tweak the last selected corner void trimrotate::KBfunc(int key) { using namespace trimrotate; int xstep, ystep; if (! Fcorner) Fcorner = KBcorner; 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 (Fcorner == 1) { // NW trimrect[0] += xstep; trimrect[1] += ystep; } if (Fcorner == 2) { // NE trimrect[2] += xstep; trimrect[1] += ystep; } if (Fcorner == 3) { // SE trimrect[2] += xstep; trimrect[3] += ystep; } if (Fcorner == 4) { // SW trimrect[0] += xstep; trimrect[3] += ystep; } 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; float drr; char text[20]; zdialog *zd = EFtrimrotate.zd; if (trimrect[0] > trimrect[2]-10) trimrect[0] = trimrect[2]-10; // sanity limits if (trimrect[1] > trimrect[3]-10) trimrect[1] = trimrect[3]-10; zdialog_fetch(zd,"lock",rlock); // w/h ratio locked if (rlock && Fcorner) { if (Fcorner < 3) trimrect[3] = trimrect[1] + 1.0 * (trimrect[2] - trimrect[0]) / trimR; else trimrect[1] = trimrect[3] - 1.0 * (trimrect[2] - trimrect[0]) / trimR; } chop = 0; if (trimrect[0] < 0) { // look for off the edge trimrect[0] = 0; // after corner move chop = 1; } if (trimrect[2] > E3ww) { trimrect[2] = E3ww; chop = 2; } if (trimrect[1] < 0) { trimrect[1] = 0; chop = 3; } if (trimrect[3] > E3hh) { trimrect[3] = E3hh; chop = 4; } if (rlock && chop) { // keep ratio if off edge if (chop < 3) trimrect[3] = trimrect[1] + 1.0 * (trimrect[2] - trimrect[0]) / trimR; else trimrect[2] = trimrect[0] + 1.0 * (trimrect[3] - trimrect[1]) * trimR; } if (trimrect[0] > trimrect[2]-10) trimrect[0] = trimrect[2]-10; // sanity limits if (trimrect[1] > trimrect[3]-10) trimrect[1] = trimrect[3]-10; if (trimrect[0] < 0) trimrect[0] = 0; // keep within visible area if (trimrect[2] > E3ww) trimrect[2] = E3ww; if (trimrect[1] < 0) trimrect[1] = 0; if (trimrect[3] > E3hh) trimrect[3] = E3hh; trimsize[0] = FPR * (trimrect[2] - trimrect[0]); // new rectangle dimensions trimsize[1] = FPR * (trimrect[3] - trimrect[1]); drr = 1.0 * trimsize[0] / trimsize[1]; // new w/h ratio if (! rlock) trimR = drr; zdialog_stuff(zd,"width",trimsize[0]); // stuff width, height, ratio zdialog_stuff(zd,"height",trimsize[1]); snprintf(text,20,"%.2f ",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 (trimrect[0] < 0) trimrect[0] = 0; // keep within visible area if (trimrect[2] > E3ww) trimrect[2] = E3ww; if (trimrect[1] < 0) trimrect[1] = 0; if (trimrect[3] > E3hh) trimrect[3] = E3hh; if (ptrimrect[0] < trimrect[0]) ox1 = ptrimrect[0]; // outer rectangle else ox1 = trimrect[0]; if (ptrimrect[2] > trimrect[2]) ox2 = ptrimrect[2]; else ox2 = trimrect[2]; if (ptrimrect[1] < trimrect[1]) oy1 = ptrimrect[1]; else oy1 = trimrect[1]; if (ptrimrect[3] > trimrect[3]) oy2 = ptrimrect[3]; else oy2 = trimrect[3]; if (ptrimrect[0] > trimrect[0]) nx1 = ptrimrect[0]; // inner rectangle else nx1 = trimrect[0]; if (ptrimrect[2] < trimrect[2]) nx2 = ptrimrect[2]; else nx2 = trimrect[2]; if (ptrimrect[1] > trimrect[1]) ny1 = ptrimrect[1]; else ny1 = trimrect[1]; if (ptrimrect[3] < trimrect[3]) ny2 = ptrimrect[3]; else ny2 = trimrect[3]; 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; 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 < trimrect[0] || px >= trimrect[2] || // px >= 15.10 py < trimrect[1] || py >= trimrect[3]) { 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); } 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 < trimrect[0] || px >= trimrect[2] || py < trimrect[1] || py >= trimrect[3]) { 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 < trimrect[0] || px >= trimrect[2] || py < trimrect[1] || py >= trimrect[3]) { 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 < trimrect[0] || px >= trimrect[2] || py < trimrect[1] || py >= trimrect[3]) { pix3[0] = pix1[0] / 2; pix3[1] = pix1[1] / 2; pix3[2] = pix1[2] / 2; } else memcpy(pix3,pix1,pcc); } Fpaint3(ox1,oy1,ox2-ox1,ny1-oy1); // 4 updated rectangles Fpaint3(nx2,oy1,ox2-nx2,oy2-oy1); Fpaint3(ox1,ny2,ox2-ox1,oy2-ny2); Fpaint3(ox1,oy1,nx1-ox1,oy2-oy1); drawlines(); // draw trim rectangle etc. ptrimrect[0] = trimrect[0]; // set prior trim rectangle ptrimrect[2] = trimrect[2]; // from current trim rectangle ptrimrect[1] = trimrect[1]; ptrimrect[3] = trimrect[3]; Fpaint2(); return; } // final trim - cut margins off the full size image // E3 is the input image, rotated and with black edges cropped if chosen // E3 is the output image from user trim rectangle // FPR should be 1.0 and E3 should be full size 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); // erase trim rectangle erase_topcircles(); // and target circle if (trimrect[2] > E3ww) trimrect[2] = E3ww; // final check if (trimrect[3] > E3hh) trimrect[3] = E3hh; trimsize[0] = trimrect[2] - trimrect[0]; // new rectangle dimensions trimsize[1] = trimrect[3] - trimrect[1]; PXM_free(E9pxm); E9pxm = PXM_make(trimsize[0],trimsize[1],nc); // new pixmap with requested size for (py1 = trimrect[1]; py1 < trimrect[3]; py1++) // copy E3 (rotated) to new size for (px1 = trimrect[0]; px1 < trimrect[2]; px1++) { px2 = px1 - trimrect[0]; py2 = py1 - trimrect[1]; pix3 = PXMpix(E3pxm,px1,py1); pix9 = PXMpix(E9pxm,px2,py2); memcpy(pix9,pix3,pcc); } if (! resource_lock(Fpaintlock)) return; // 15.02 PXM_free(E3pxm); E3pxm = E9pxm; E9pxm = 0; resource_unlock(Fpaintlock); E3ww = E3pxm->ww; // update E3 dimensions E3hh = E3pxm->hh; FPR = 1.0 * (E0ww + E0hh) / (E3ww + E3hh); // full image / preview ratio CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); return; } // rotate function // E3 = E1 with rotation and edges cropped if chosen. // E9 = E3 copy: used as source for darkened pixels in E3. void trimrotate::rotate_func() { using namespace trimrotate; int px3, py3, px9, py9; int wwcut, hhcut, ww, hh; float trim_angle, radians; float *pix3, *pix9; zdialog *zd = EFtrimrotate.zd; int nc = E3pxm->nc, pcc = nc * sizeof(float); ///zmainloop(); // bugfix 15.09 if (! resource_lock(Fpaintlock)) return; // 15.02 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); PXM_free(E9pxm); // E1 >> E9 E9pxm = PXM_copy(E1pxm); } if (rotate_goal != rotate_angle || rotatecrop2 != rotatecrop1) { rotate_angle = rotate_goal; rotatecrop2 = rotatecrop1; // remember autocrop setting PXM_free(E9pxm); E9pxm = PXM_rotate(E1pxm,rotate_angle); // E9 is rotated E1 if (rotatecrop1) // edge cropping is chosen { trim_angle = fabsf(rotate_goal); while (trim_angle > 45) trim_angle -= 90; radians = fabsf(trim_angle / 57.296); wwcut = int(E9pxm->hh * sinf(radians) + 1); // amount to crop off edges hhcut = int(E9pxm->ww * sinf(radians) + 1); if (wwcut > E9pxm->ww / 2 - 10) // do not cut 100% wwcut = E9pxm->ww / 2 - 10; if (hhcut > E9pxm->hh / 2 - 10) hhcut = E9pxm->hh / 2 - 10; ww = E9pxm->ww - 2 * wwcut; hh = E9pxm->hh - 2 * hhcut; PXM_free(E3pxm); E3pxm = PXM_make(ww,hh,nc); // E3 = cropped E9 for (py3 = 0; py3 < hh; py3++) // copy pixels E9 > E3 for (px3 = 0; px3 < ww; px3++) { pix3 = PXMpix(E3pxm,px3,py3); px9 = px3 + wwcut; py9 = py3 + hhcut; pix9 = PXMpix(E9pxm,px9,py9); memcpy(pix3,pix9,pcc); } PXM_free(E9pxm); // E9 = E3 E9pxm = PXM_copy(E3pxm); } else { // no edge cropping PXM_free(E3pxm); // E3 = E9 E3pxm = PXM_copy(E9pxm); } } resource_unlock(Fpaintlock); E3ww = E3pxm->ww; // update E3 dimensions E3hh = E3pxm->hh; if (trimrect[2] > E3ww) trimrect[2] = E3ww; // contract trim rectangle if needed if (trimrect[3] > E3hh) trimrect[3] = E3hh; trimsize[0] = FPR * (trimrect[2] - trimrect[0]); // new rectangle dimensions trimsize[1] = FPR * (trimrect[3] - trimrect[1]); zdialog_stuff(zd,"width",trimsize[0]); // stuff width, height zdialog_stuff(zd,"height",trimsize[1]); Fpaintnow(); drawlines(); // draw trim rectangle etc. ptrimrect[0] = 0; // set prior trim rectangle ptrimrect[1] = 0; ptrimrect[2] = E3ww; // = 100% of image ptrimrect[3] = E3hh; CEF->Fmods++; return; } // perform final rotate and trim on full-size image void trimrotate::trim_fullsize() { using namespace trimrotate; edit_fullsize(); // restore full size E1 trimrect[0] = FPR * trimrect[0]; // full scale trim rectangle trimrect[1] = FPR * trimrect[1]; trimrect[2] = FPR * trimrect[2]; trimrect[3] = FPR * trimrect[3]; FPR = 1.0; trimsize[0] = trimrect[2] - trimrect[0]; // final trim size trimsize[1] = trimrect[3] - trimrect[1]; rotate_angle = 0; // rotate full size image rotate_func(); // E3 = E9 = rotated and cropped E1 trim_final(); // trim E3 from user trim rectangle return; } // draw lines on image: trim rectangle, guidelines, gridlines void trimrotate::drawlines() { using namespace trimrotate; int px, py, radius; Ntoplines = 4; // outline trim rectangle toplines[0].x1 = trimrect[0]; toplines[0].y1 = trimrect[1]; toplines[0].x2 = trimrect[2]; toplines[0].y2 = trimrect[1]; toplines[0].type = 1; toplines[1].x1 = trimrect[2]; toplines[1].y1 = trimrect[1]; toplines[1].x2 = trimrect[2]; toplines[1].y2 = trimrect[3]; toplines[1].type = 1; toplines[2].x1 = trimrect[2]; toplines[2].y1 = trimrect[3]; toplines[2].x2 = trimrect[0]; toplines[2].y2 = trimrect[3]; toplines[2].type = 1; toplines[3].x1 = trimrect[0]; toplines[3].y1 = trimrect[3]; toplines[3].x2 = trimrect[0]; toplines[3].y2 = trimrect[1]; 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); // draw trim rectangle and guidelines erase_topcircles(); px = 0.5 * (ptrimrect[0] + ptrimrect[2]); py = 0.5 * (ptrimrect[1] + ptrimrect[3]); radius = 0.02 * Mscale * (E3ww + E3hh); Fpaint3(px-radius,py-radius,2*radius,2*radius); px = 0.5 * (trimrect[0] + trimrect[2]); py = 0.5 * (trimrect[1] + trimrect[3]); add_topcircle(px,py,radius/2); draw_topcircles(); if (gridsettings[currgrid][GON]) Fpaint2(); // refresh gridlines, delayed return; } // auto-trim image - set trim rectangle to exclude transparent regions void trimrotate::autotrim() // 15.06 { 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 15.09 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 15.09 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; zmainloop(); } 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; } trimrect[0] = px1; // set parameters for trim function trimrect[2] = px2; trimrect[1] = py1; trimrect[3] = py2; return; } /**************************************************************************/ // Upright a rotated image: -90, +90, 180 degrees. // This is not an edit transaction: file is replaced and re-opened. zdialog *zdupright = 0; void m_upright(GtkWidget *, cchar *menu) // 15.03.1 { int upright_dialog_event(zdialog *zd, cchar *event); F1_help_topic = "upright"; if (checkpend("all")) return; Fblock = 1; // 15.11 if (zdupright) return; zdialog *zd = zdialog_new(ZTX("Upright Image"),Mwin,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); 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"); zdupright = zd; return; } // dialog event and completion function int upright_dialog_event(zdialog *zd, cchar *event) // 15.03.1 { int angle = 0, err; char *upfile; char orientation = 0, *ppv[2]; cchar *exifkey[1] = { exif_orientation_key }; cchar *exifdata[1]; char mode = FGW; if (strmatch(event,"escape")) event = "cancel"; // escape = cancel 15.07 if (zd->zstat) { // [x] button zdialog_free(zd); Fblock = 0; // 15.11 zdupright = 0; return 1; } if (strmatch(event,"cancel")) { // cancel button zdialog_free(zd); Fblock = 0; // 15.11 zdupright = 0; return 1; } if (! strstr("upright -90 +90 180",event)) return 1; // ignore other events if (clicked_file) upfile = clicked_file; // get clicked file from gallery else if (curr_file) upfile = curr_file; // else use current file else return 1; clicked_file = 0; if (! curr_file || ! strmatch(upfile,curr_file)) { // make upright file current err = f_open(upfile,0,0,1); if (err) return 1; zfree(upfile); } 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; if (strmatch(event,"-90")) angle = -90; if (strmatch(event,"180")) angle = 180; if (! angle) return 1; // ignore other events if (! resource_lock(Fpaintlock)) return 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; } resource_unlock(Fpaintlock); err = f_save(curr_file,curr_file_type,curr_file_bpc,1); PXM_free(E0pxm); exifdata[0] = (char *) ""; // remove EXIF key "orientation" err = exif_put(curr_file,exifkey,exifdata,1); f_open(curr_file); // open rotated file update_image_index(curr_file); // update index data gtk_window_present(MWIN); if (mode == 'G') { gallery(curr_file,"paint"); // update gallery m_viewmode(0,"G"); } return 1; } /**************************************************************************/ // 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 15.10 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) // 15.04 { 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 15.10 if (! edit_setup(EFvoodoo2)) return; // setup edit Ffuncbusy = 1; Iww = E1pxm->ww; // image dimensions Ihh = E1pxm->hh; 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)); 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); } 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); } 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); } 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; int combo_spc; // current spline curve float brightness; // brightness input, -1 ... +1 float contrast; // contrast input, -1 ... +1 float wbalR, wbalG, wbalB; // white balance, white basis standard 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_combo(GtkWidget *, cchar *menu) { using namespace combo_names; int combo_dialog_event(zdialog* zd, cchar *event); void combo_curvedit(int spc); void * combo_thread(void *); F1_help_topic = "retouch_combo"; EFcombo.menuname = menu; EFcombo.menufunc = m_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 15.10 EFcombo.threadfunc = combo_thread; if (! edit_setup(EFcombo)) return; // setup edit /*** _____________________________________________________ | Retouch Combo | | _________________________________________________ | | | | | // 5 curves are maintained: | | | | // curve 0: current display curve | | | | // 1: curve for all colors | | curve edit area | | // 2,3,4: red, green, blue | | | | | | | | | |_________________________________________________| | | |_________________________________________________| | // 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] Brightness Distribution | | [x] Click for white balance or black level | // click gray/white spot for white balance | | | Settings [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",0,"space=3"); zdialog_add_widget(zd,"label","space","hbcolor",0,"space=5"); zdialog_add_widget(zd,"vbox","vbcolor1","hbcolor",0,"homog"); zdialog_add_widget(zd,"label","space","hbcolor",0,"space=3"); zdialog_add_widget(zd,"vbox","vbcolor2","hbcolor",0,"homog|expand"); zdialog_add_widget(zd,"label","space","hbcolor",0,"space=3"); zdialog_add_widget(zd,"vbox","vbcolor3","hbcolor",0,"homog"); zdialog_add_widget(zd,"label","space","hbcolor",0,"space=5"); 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","click","hbclick",ZTX("Click for white balance or black level"),"space=3"); zdialog_add_widget(zd,"hbox","hbset","dialog"); 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); // 15.01 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; // diagonal fixed line, neutral curve sd->xscale[0][0] = 0.00; sd->yscale[0][0] = 0.00; sd->xscale[1][0] = 1.00; sd->yscale[1][0] = 1.00; 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; // 15.10 sd->apx[ii][0] = sd->apy[ii][0] = 0.01; sd->apx[ii][1] = sd->apy[ii][1] = 0.50; // curve 0 = overall brightness sd->apx[ii][2] = sd->apy[ii][2] = 0.99; // curve 1/2/3 = R/G/B adjustment splcurve_generate(sd,ii); } sd->Nspc = 4; // 4 curves sd->fact[0] = 1; // curve 0 active 15.10 combo_spc = 0; // current active curve 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 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 satlevel = 0; // neutral saturation areaemph = 0.5; // even dark/bright area emphasis for (int ii = 0; ii < 256; ii++) emphcurve[ii] = 1; zdialog_stuff(zd,"click",0); // reset mouse click status zdialog_resize(zd,300,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, jj, nlo, nhi; int Fapply = 0; if (strmatch(event,"done")) zd->zstat = 3; // apply and quit if (strmatch(event,"enter")) zd->zstat = 3; if (strmatch(event,"cancel")) zd->zstat = 4; if (strmatch(event,"escape")) zd->zstat = 4; // escape = cancel 15.07 if (strmatch(event,"apply")) Fapply = 1; // from script 15.10 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; 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 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] = sd->apy[ii][0] = 0.01; sd->apx[ii][1] = sd->apy[ii][1] = 0.50; sd->apx[ii][2] = sd->apy[ii][2] = 0.99; splcurve_generate(sd,ii); } combo_spc = 0; // current curve zdialog_stuff(zd,"all",1); edit_reset(); // restore initial image event = "update"; // trigger graph update } if (zd->zstat == 2) // [prev] restore previous settings { zd->zstat = 0; // keep dialog active edit_load_prev_widgets(&EFcombo); // 15.10 Fapply = 1; // trigger apply event } if (zd->zstat) // [done] or [cancel] { freeMouse(); if (zd->zstat == 3) { // [done] edit_fullsize(); // get full size image signal_thread(); edit_save_last_widgets(&EFcombo); // 15.10 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,"click")) { // toggle mouse click input zdialog_fetch(zd,"click",ii); if (ii) takeMouse(combo_mousefunc,dragcursor); // on: connect mouse function else freeMouse(); // off: free mouse return 1; } if (strmatch(event,"dist")) { // distribution checkbox zdialog_fetch(zd,"dist",Fdist); event = "update"; // 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); combo_spc = 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; zdialog_stuff(zd,"all",1); // active curve is "all" combo_spc = 0; 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.01 || dx >= 0.99) continue; dx = 0.5 - fabsf(dx - 0.5); // 0 ... 0.5 ... 0 dx = 0.5 * sqrtf(dx); // 0 ... 0.35 ... 0 (more rounded shape) dy = dx * dbrite; 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 (strmatch(event,"contrast")) // contrast slider, 0 ... 1 { cont0 = contrast; zdialog_fetch(zd,"contrast",contrast); dcont = contrast - cont0; zdialog_stuff(zd,"all",1); // active curve is "all" combo_spc = 0; sd->fact[0] = 1; for (ii = 1; ii < 4; ii++) sd->fact[ii] = 0; nlo = nhi = 0; // count nodes in x-range 0.1-0.4 15.07 for (ii = 0; ii < sd->nap[0]; ii++) { // and x-range 0.6-0.9 dx = sd->apx[0][ii]; if (dx > 0.1 && dx < 0.4) nlo++; if (dx > 0.6 && dx < 0.9) nhi++; } if (nlo == 0) { // add node in low range if needed dx = 0.25; dy = sd->yval[0][250]; splcurve_addnode(sd,0,dx,dy); } if (nhi == 0) { // add node in high range if needed dx = 0.75; dy = sd->yval[0][750]; splcurve_addnode(sd,0,dx,dy); } for (jj = 0; jj < sd->nap[0]; jj++) // update curve 0 nodes from slider { dx = sd->apx[0][jj]; // 0 ... 0.5 ... 1 if (dx <= 0.01 || dx >= 0.99) continue; if (dx < 0.5) dx = -0.25 + fabsf(dx - 0.25); // 0 ... -0.25 ... 0 else dx = +0.25 - fabsf(dx - 0.75); // 0 ... +0.25 ... 0 dy = dx * dcont; dy += sd->apy[0][jj]; if (dy < 0) dy = 0; if (dy > 1) dy = 1; sd->apy[0][jj] = 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,"update")) // 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 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 from mouse click position void combo_mousefunc() // mouse function { using namespace combo_names; int px, py, dx, dy, ii; float red, green, blue, rgbmean; float *ppix; spldat *sd = EFcombo.curves; // active curve, 1-4 zdialog *zd = EFcombo.zd; 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(mousetext,10,10,0,3); if (red > 60 && green > 60 && blue > 60) // assume gray/white point 15.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 } else if (red < 50 && green < 50 && blue < 50) // assume this is a black point { zdialog_stuff(zd,"all",1); // active curve is "all" combo_spc = 0; sd->fact[0] = 1; for (ii = 1; ii < 4; ii++) sd->fact[ii] = 0; sd->apx[0][0] = (red + green + blue) / 3.0 / 256.0; // set low clipping level sd->apy[0][0] = 0.0; // from black point pixel splcurve_generate(sd,0); // regenerate curve 0 gtk_widget_queue_draw(sd->drawarea); // draw curve 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 && CEF->zd) { zd_thread = CEF->zd; // signal dialog to update graph zd_thread_event = "update"; } 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 pixbrite, F1, F2; float coeff = 1000.0 / 256.0; float dold, dnew, ff; spldat *sd = EFcombo.curves; 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]; green1 = pix1[1]; blue1 = pix1[2]; // 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 curve pixbrite = red3; // use max. RGB value if (green3 > pixbrite) pixbrite = green3; if (blue3 > pixbrite) pixbrite = blue3; if (amplify == 1.0) // amplifier is neutral { ii = coeff * pixbrite; // range 0-999 for curve index ff = 255.9 * sd->yval[0][ii]; // curve "all" adjustment red3 = ff * red3 / pixbrite; // projected on each RGB color green3 = ff * green3 / pixbrite; blue3 = ff * blue3 / pixbrite; } else // use amplifier { ii = coeff * pixbrite; ff = 0.001 * ii; ff = ff + amplify * (sd->yval[0][ii] - ff); // amplify (curve - baseline) difference if (ff < 0) ff = 0; red3 = 255.9 * ff * red3 / pixbrite; green3 = 255.9 * ff * green3 / pixbrite; blue3 = 255.9 * ff * blue3 / pixbrite; } ii = coeff * red3; // add additional RGB adjustments if (ii < 0) ii = 0; if (ii > 999) ii = 999; red3 = 255.9 * sd->yval[1][ii]; // output brightness, 0-255.9 ii = coeff * green3; if (ii < 0) ii = 0; if (ii > 999) ii = 999; green3 = 255.9 * sd->yval[2][ii]; ii = coeff * blue3; if (ii < 0) ii = 0; if (ii > 999) ii = 999; blue3 = 255.9 * sd->yval[3][ii]; if (red3 < 0) red3 = 0; // stop underflow if (green3 < 0) green3 = 0; if (blue3 < 0) blue3 = 0; 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; } // select area edge blending if (sa_stat == 3 && dist < sa_blend) { dnew = 1.0 * dist / sa_blend; 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 = 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_britedist(GtkWidget *, cchar *menu) // 15.08 { using namespace britedist_names; cchar *title = ZTX("Adjust Brightness Distribution"); F1_help_topic = "britedist"; EFbrightdist.menuname = menu; EFbrightdist.menufunc = m_britedist; EFbrightdist.funcname = "brightdist"; EFbrightdist.FprevReq = 1; // preview EFbrightdist.Farea = 2; // select area usable EFbrightdist.Frestart = 1; // restart allowed EFbrightdist.Fscript = 1; // scripting supported 15.10 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,"enter")) zd->zstat = 2; if (strmatch(event,"cancel")) zd->zstat = 3; // cancel if (strmatch(event,"escape")) zd->zstat = 4; // escape = cancel 15.07 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) { // 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 = 1.0 * dist / sa_blend; // 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 } /**************************************************************************/ // flatten brightness distribution based on the distribution of nearby zones namespace zonal_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) 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 EFzonalflat; int dialog_event(zdialog* zd, cchar *event); void doflatten(); void calczones(); void initzones(); void * thread(void *); void * wthread(void *); } // menu function void m_zonal_flatten(GtkWidget *, cchar *menu) // 15.04 { using namespace zonal_flatten_names; cchar *title = ZTX("Zonal Flatten Brightness"); F1_help_topic = "zonal_flatten"; EFzonalflat.menuname = menu; EFzonalflat.menufunc = m_zonal_flatten; EFzonalflat.funcname = "zonal-flatten"; EFzonalflat.FprevReq = 1; // use preview image EFzonalflat.Farea = 2; // select area usable EFzonalflat.Frestart = 1; // restartable 15.05 EFzonalflat.FusePL = 1; // use with paint/lever edits OK EFzonalflat.Fscript = 1; // scripting supported 15.10 EFzonalflat.threadfunc = thread; if (! edit_setup(EFzonalflat)) return; // setup edit /*** ______________________________________ | Zonal Flatten Brightness | | | | Zones [123|-+] [Apply] | | Flatten =========[]============ NN | | Deband Dark =========[]========= NN | | Deband Bright ==========[]====== NN | | | | [Done] [Cancel] | |______________________________________| ***/ zdialog *zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); EFzonalflat.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,"spin","zones","hb1","20|200|1|40"); 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 zonal_flatten_names::dialog_event(zdialog *zd, cchar *event) { using namespace zonal_flatten_names; if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"enter")) zd->zstat = 1; if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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) 15.10 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 zonal_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 zonal_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; NZ = Zrows * Zcols; // NZ matching rows x cols if (dNZ > 0 && NZ <= pNZ) { // no increase, try again if (NZ >= 199) 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 zonal_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 = (uint8 *) zmalloc(Iww * Ihh * 9 * sizeof(uint8)); 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); 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 } 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 } return; } // adjust brightness distribution thread function void * zonal_flatten_names::thread(void *) { using namespace zonal_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 * zonal_flatten_names::wthread(void *arg) { using namespace zonal_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 = red1 + green1 + blue1; // input pixel brightness 0-767.9 bright *= 1000.0 / 768.0; // 0-999.9 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 15.05 fnew2 = 1 - 0.01 * deband2 * 0.001 * bright; // attenuate bright pixels 15.05 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 = 1.0 * dist / sa_blend; // 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 } /************************************************************************** Image Tone Mapping function enhance local contrast as opposed to overall contrast methodology: get brightness gradients for each pixel in 4 directions: SE SW NE NW amplify gradients using the edit curve (x-axis range 0-max. 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 overamplified new pixel brightness = average from 4 calculated brightness surfaces ***************************************************************************/ namespace tonemap_names { float *brmap1, *brmap2; float *brmap4[4]; int contrast99; float amplify; int ww, hh; editfunc EFtonemap; void Tmap_initz(zdialog *zd); int Tmap_dialog_event(zdialog *zd, cchar *event); void Tmap_curvedit(int); void * Tmap_thread(void *); void * Tmap_wthread1(void *arg); void * Tmap_wthread2(void *arg); void * Tmap_wthread3(void *arg); } void m_tonemap(GtkWidget *, cchar *menu) { using namespace tonemap_names; F1_help_topic = "tone_mapping"; cchar *title = ZTX("Tone Mapping"); EFtonemap.menuname = menu; EFtonemap.menufunc = m_tonemap; EFtonemap.funcname = "tonemap"; EFtonemap.Farea = 2; // select area usable EFtonemap.Frestart = 1; // restart allowed EFtonemap.FusePL = 1; // use with paint/lever edits OK EFtonemap.Fscript = 1; // scripting supported 15.10 EFtonemap.threadfunc = Tmap_thread; if (! edit_setup(EFtonemap)) 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); EFtonemap.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,Tmap_curvedit); EFtonemap.curves = sd; sd->Nspc = 1; sd->fact[0] = 1; // 15.10 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 Tmap_initz(zd); // initialize return; } // initialization for new image file void tonemap_names::Tmap_initz(zdialog *zd) { using namespace tonemap_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,Tmap_dialog_event,"save"); // run dialog - parallel amplify = 0; return; } // dialog event and completion callback function int tonemap_names::Tmap_dialog_event(zdialog *zd, cchar *event) { using namespace tonemap_names; spldat *sd = EFtonemap.curves; char text[8]; if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"enter")) zd->zstat = 1; if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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 15.10 Tmap_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 tonemap_names::Tmap_curvedit(int) { signal_thread(); return; } // thread function void * tonemap_names::Tmap_thread(void *) { using namespace tonemap_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(Tmap_wthread1,&Nval[ii]); wait_wthreads(); // wait for completion for (ii = 0; ii < 4; ii++) // start working threads 2 (4) start_wthread(Tmap_wthread2,&Nval[ii]); wait_wthreads(); // wait for completion for (ii = 0; ii < NWT; ii++) // start working threads 3 (SMP CPUs) start_wthread(Tmap_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 * tonemap_names::Tmap_wthread1(void *arg) { using namespace tonemap_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 = EFtonemap.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 * tonemap_names::Tmap_wthread2(void *arg) { using namespace tonemap_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 * tonemap_names::Tmap_wthread3(void *arg) { using namespace tonemap_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 = 1.0 * dist / sa_blend; // 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, stop g++ warning } /**************************************************************************/ // 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; int resize_dialog_event(zdialog *zd, cchar *event); void * resize_thread(void *); void m_resize(GtkWidget *, cchar *menu) { 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 15.10 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,"spin","wpix","vb12","20|20000|1|20"); zdialog_add_widget(zd,"spin","hpix","vb12","20|20000|1|20"); zdialog_add_widget(zd,"label","labpct","vb13",Bpercent); zdialog_add_widget(zd,"spin","wpct","vb13","1|500|0.1|100"); zdialog_add_widget(zd,"spin","hpct","vb13","1|500|0.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; // 15.10 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 15.10 { int lock, nn; float wpct1, hpct1, ratio; char rtext[12]; if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"enter")) zd->zstat = 1; if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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) resize_lockratio = 1.0 * resize_ww1 / resize_hh1; // if on, update lock ratio to hold 15.10 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 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 15.10 snprintf(rtext,12,"%.3f",ratio); zdialog_stuff(zd,"ratio",rtext); resize_lockratio = ratio; // update lock ratio 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 if (resource_lock(Fpaintlock)) { pxmtemp2 = E3pxm; E3pxm = pxmtemp1; PXM_free(pxmtemp2); resource_unlock(Fpaintlock); CEF->Fmods++; CEF->Fsaved = 0; } else PXM_free(pxmtemp1); Fpaint2(); } return 0; } /**************************************************************************/ // flip an image horizontally or vertically editfunc EFflip; void m_flip(GtkWidget *, cchar *) { int flip_dialog_event(zdialog *zd, cchar *event); F1_help_topic = "flip_image"; EFflip.menufunc = m_flip; EFflip.funcname = "flip"; EFflip.Frestart = 1; // allow restart if (! edit_setup(EFflip)) return; // setup edit zdialog *zd = zdialog_new(ZTX("Flip"),Mwin,Bdone,Bcancel,null); EFflip.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"button","horz","hb1",ZTX("horizontal"),"space=5"); zdialog_add_widget(zd,"button","vert","hb1",ZTX("vertical"),"space=5"); zdialog_run(zd,flip_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int flip_dialog_event(zdialog *zd, cchar *event) { int flip_horz(); int flip_vert(); if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"enter")) zd->zstat = 1; if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (zd->zstat) // dialog complete { if (zd->zstat == 1) edit_done(0); else edit_cancel(0); return 1; } if (strmatch(event,"horz")) flip_horz(); if (strmatch(event,"vert")) flip_vert(); return 1; } int flip_horz() { int px, py; float *pix3, *pix9; int nc = E3pxm->nc, pcc = nc * sizeof(float); if (! resource_lock(Fpaintlock)) return 0; // 15.02 E9pxm = PXM_copy(E3pxm); for (py = 0; py < E3pxm->hh; py++) for (px = 0; px < E3pxm->ww; px++) { pix3 = PXMpix(E3pxm,px,py); // image9 = flipped image3 pix9 = PXMpix(E9pxm,E3pxm->ww-1-px,py); memcpy(pix9,pix3,pcc); } PXM_free(E3pxm); // image9 >> image3 E3pxm = E9pxm; E9pxm = 0; resource_unlock(Fpaintlock); CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); return 0; } int flip_vert() { int px, py; float *pix3, *pix9; int nc = E3pxm->nc, pcc = nc * sizeof(float); if (! resource_lock(Fpaintlock)) return 0; // 15.02 E9pxm = PXM_copy(E3pxm); for (py = 0; py < E3pxm->hh; py++) for (px = 0; px < E3pxm->ww; px++) { pix3 = PXMpix(E3pxm,px,py); // image9 = flipped image3 pix9 = PXMpix(E9pxm,px,E3pxm->hh-1-py); memcpy(pix9,pix3,pcc); } PXM_free(E3pxm); // image9 >> image3 E3pxm = E9pxm; E9pxm = 0; resource_unlock(Fpaintlock); CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); return 0; } /**************************************************************************/ // write text on top of the image namespace writetext { #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 EFwritetext; } // menu function void m_write_text(GtkWidget *, cchar *menu) { using namespace writetext; cchar *title = ZTX("Write Text on Image"); cchar *tip = ZTX("Enter text, click/drag on image, right click to remove"); F1_help_topic = "add_text"; // user guide topic EFwritetext.menufunc = m_write_text; EFwritetext.funcname = "write_text"; EFwritetext.Farea = 1; // select area ignored EFwritetext.Frestart = 1; // allow restart if (! edit_setup(EFwritetext)) return; // setup edit /*** ____________________________________________________________________ | Write Text on Image | | | | Enter text, click/drag on image, right click to remove. | | | | Text [____________________________________________________] | text | Use metadata key [________________________________] [Fetch] | metakey Bfetch | Use text file [Open] [Save] | Bopen Bsave | | | [Font] [FreeSans_________] Size [ 44|v] | Bfont fontname fontsize | | | color transparency 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] | Bclear Breplace Bversion Bnext |____________________________________________________________________| Bapply Bdone Bcancel [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); EFwritetext.zd = zd; EFwritetext.mousefunc = mousefunc; EFwritetext.menufunc = m_write_text; // allow restart zdialog_add_widget(zd,"label","tip","dialog",tip,"space=5"); 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","text","expand|wrap"); zdialog_add_widget(zd,"hbox","hbmeta","dialog"); zdialog_add_widget(zd,"label","labmeta","hbmeta",ZTX("Use metadata key"),"space=5"); zdialog_add_widget(zd,"entry","metakey","hbmeta",0,"space=2|expand"); zdialog_add_widget(zd,"button",Bfetch,"hbmeta",Bfetch); zdialog_add_widget(zd,"hbox","hbfile","dialog"); zdialog_add_widget(zd,"label","labfile","hbfile",ZTX("Use text file"),"space=3"); zdialog_add_widget(zd,"button",Bopen,"hbfile",Bopen); zdialog_add_widget(zd,"button",Bsave,"hbfile",Bsave); zdialog_add_widget(zd,"button",Bclear,"hbfile",Bclear,"space=30"); zdialog_add_widget(zd,"hbox","hbfont","dialog"); zdialog_add_widget(zd,"button",Bfont,"hbfont",Bfont); zdialog_add_widget(zd,"entry","fontname","hbfont","FreeSans","space=2|expand"); zdialog_add_widget(zd,"label","labfsize","hbfont",Bsize,"space=2"); zdialog_add_widget(zd,"spin","fontsize","hbfont","8|500|1|40"); 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",Btransparency); zdialog_add_widget(zd,"spin","fgtransp","vbattr3","0|100|1|0"); zdialog_add_widget(zd,"spin","bgtransp","vbattr3","0|100|1|0"); zdialog_add_widget(zd,"spin","totransp","vbattr3","0|100|1|0"); zdialog_add_widget(zd,"spin","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,"spin","towidth","vbattr4","0|30|1|0"); zdialog_add_widget(zd,"spin","shwidth","vbattr4","0|50|1|0"); zdialog_add_widget(zd,"label","labw","vbattr5",Bangle); zdialog_add_widget(zd,"spin","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,"spin","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 writetext::dialog_event(zdialog *zd, cchar *event) { using namespace writetext; 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 (strmatch(event,"escape")) zd->zstat = 7; // escape = cancel 15.07 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_write_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_write_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_write_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_write_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 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 zmainloop(); return 1; } // mouse function, set position for text on image void writetext::mousefunc() { using namespace writetext; 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 writetext::write(int mode) { using namespace writetext; float *pix1, *pix3; uint8 *pixt1; int px1, py1, px3, py3, done; float e3part; 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); 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); // update window to erase old text textpresent = 0; // mark no text present CEF->Fmods--; } 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; pixt1 = PXBpix(attr.pxb_text,px1,py1); // copy-from text pixel pix3 = PXMpix(E3pxm,px3,py3); // copy-to image pixel e3part = pixt1[3] / 256.0; // image part visible through text pix3[0] = pixt1[0] + e3part * pix3[0]; // combine text part + image part pix3[1] = pixt1[1] + e3part * pix3[1]; pix3[2] = pixt1[2] + e3part * pix3[2]; } if (textpresent) { Fpaint3(orgx1,orgy1,ww1,hh1); // update window to erase old text textpresent = 0; CEF->Fmods--; } // (updates together to reduce flicker) Fpaint3(orgx2,orgy2,ww2,hh2); // update window for new text 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",writetext_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",writetext_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. 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; zthreadcrash(); // thread usage not allowed 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(Cdrawin,null); 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,hh,0); // create PXB 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,py); 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; 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 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 writeline { 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 EFwriteline; } void m_write_line(GtkWidget *, cchar *menu) { using namespace writeline; 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 EFwriteline.menufunc = m_write_line; EFwriteline.funcname = "write_line"; EFwriteline.Farea = 1; // select area ignored if (! edit_setup(EFwriteline)) return; // setup edit /*** ____________________________________________________________ | Write Line or Arrow on Image | | | | Enter line or arrow properties in dialog, | | click/drag on image, right click to remove. | | | | Line length [____|+-] width [____|+-] | | Arrow head [x] left [x] right | | | | color transparency 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("Write Line or Arrow on Image"),Mwin,Bapply,Bdone,Bcancel,null); EFwriteline.zd = zd; EFwriteline.mousefunc = mousefunc; EFwriteline.menufunc = m_write_line; // allow restart 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,"spin","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,"spin","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",Btransparency); zdialog_add_widget(zd,"spin","fgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"spin","bgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"spin","totransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"spin","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,"spin","towidth","vbcol4","0|30|1|0"); zdialog_add_widget(zd,"spin","shwidth","vbcol4","0|50|1|0"); zdialog_add_widget(zd,"label","labw","vbcol5",Bangle); zdialog_add_widget(zd,"spin","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,"spin","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 writeline::dialog_event(zdialog *zd, cchar *event) { using namespace writeline; if (strmatch(event,"done")) zd->zstat = 2; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 3; // cancel if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 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_write_line(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,"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 zmainloop(); return 1; } // mouse function, set new position for line on image void writeline::mousefunc() { using namespace writeline; float ax1, ay1, ax2, ay2; // line/arrow end points float angle, rad, l2; float amx, amy; float d1, d2; 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; // move the closest line endpoint to the mouse position and leave the other endpoint fixed if (! linepresent) { orgx2 = mpx; orgy2 = mpy; write(1); return; } 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; rad = asinf((ay1-ay2) / d2); angle = -57.296 * rad; if (mpx > ax2) 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); } if (d2 < d1) { // move ax2/ay2 end to mouse ax1 += orgx1; ay1 += orgy1; ax2 = mpx; ay2 = mpy; attr.length = d1 + 0.5; rad = asinf((ay1-ay2) / d1); 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 writeline::write(int mode) { using namespace writeline; float *pix1, *pix3; uint8 *pixl1; int px1, py1, px3, py3, done; float e3part; int nc = E1pxm->nc, pcc = nc * sizeof(float); 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); // update window to erase old line linepresent = 0; // mark no line present } 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; pixl1 = PXBpix(attr.pxb_line,px1,py1); // copy-from line pixel pix3 = PXMpix(E3pxm,px3,py3); // copy-to image pixel e3part = pixl1[3] / 256.0; // image part visible through line pix3[0] = pixl1[0] + e3part * pix3[0]; // combine line part + image part pix3[1] = pixl1[1] + e3part * pix3[1]; pix3[2] = pixl1[2] + e3part * pix3[2]; } if (linepresent) { Fpaint3(orgx1,orgy1,ww1,hh1); // update window to erase old line linepresent = 0; } // (updates together to reduce flicker) Fpaint3(orgx2,orgy2,ww2,hh2); // update window for new line 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; } /**************************************************************************/ // 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; zthreadcrash(); // thread usage not allowed 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 15.08 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) { edit_fullsize(); // use full-size image signal_thread(); wait_thread_idle(); } /*** ____________________________________________ | Press F1 for help | | Edit Function must be active | | mouse radius [___|v] | | power: center [___|v] edge [___|v] | | [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",ZTX("mouse radius"),"space=5"); zdialog_add_widget(zdsela,"spin","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,"spin","center","hbt","0|100|1|50"); zdialog_add_widget(zdsela,"label","labte","hbt",Bedge,"space=5"); zdialog_add_widget(zdsela,"spin","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 (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (zd->zstat) // done or cancel { freeMouse(); // disconnect mouse function if (CEF) zdialog_send_event(CEF->zd,"done"); // complete edit 15.08 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); // 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 > E1pxm->ww-1) continue; // off the image edge if (py < 0 || py > E1pxm->hh-1) continue; ii = E1pxm->ww * py + px; rad = sqrt(rad2); power = cpower + rad / radius * (epower - cpower); // power at pixel radius if (Mbutton == 1) // left mouse button { // increase edit power sa_pixmap[ii] += 2.0 * power; // make paint edit 2x faster if (sa_pixmap[ii] > sa_blend) sa_pixmap[ii] = sa_blend; } if (Mbutton == 3) // right mouse button { // weaken edit power if (sa_pixmap[ii] <= power) sa_pixmap[ii] = 0; // erase is half as fast as paint else sa_pixmap[ii] -= power; } } zdialog_send_event(CEF->zd,"blendwidth"); // notify edit dialog draw_mousecircle(Mxposn,Myposn,radius,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 15.08 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) { edit_fullsize(); // use full-size image signal_thread(); wait_thread_idle(); } /*** 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","hbb","dialog",0,"space=10"); zdialog_add_widget(zdsela,"button","b +++","hbb","+++","space=3"); zdialog_add_widget(zdsela,"button","b ---","hbb","‒ ‒ ‒","space=3"); zdialog_add_widget(zdsela,"button","b +-", "hbb"," + ‒ ","space=3"); zdialog_add_widget(zdsela,"button","b -+", "hbb"," ‒ + ","space=3"); zdialog_add_widget(zdsela,"button","b +-+","hbb","+ ‒ +","space=3"); zdialog_add_widget(zdsela,"button","b -+-","hbb","‒ + ‒","space=3"); 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; // 15.10 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 px, py, xval, yval, lever; float *pixel0, *pixel1, *pixel2, *pixel3, *pixel4; spldat *sd = leveds_curve; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (zd->zstat) { // done or cancel 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 15.08 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); // 15.09 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 (strmatchN(event,"b ",2)) { // button to move entire curve for (ii = 0; ii < sd->nap[0]; ii++) { px = sd->apx[0][ii]; py = sd->apy[0][ii]; if (strmatch(event,"b +++")) py += 0.1; if (strmatch(event,"b ---")) py -= 0.1; if (strmatch(event,"b +-")) py += 0.1 - 0.2 * px; if (strmatch(event,"b -+")) py -= 0.1 - 0.2 * px; if (strmatch(event,"b +-+")) py -= 0.05 - 0.2 * fabsf(px-0.5); if (strmatch(event,"b -+-")) py += 0.05 - 0.2 * fabsf(px-0.5); if (py > 1) py = 1; if (py < 0) py = 0; sd->apy[0][ii] = py; } event = "edit"; } if (strmatch(event,"edit")) { splcurve_generate(sd,0); // regenerate the curve gtk_widget_queue_draw(sd->drawarea); } if (! CEF) return 1; // edit canceled 15.08 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 *) // new 15.01 { 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_zuserdir()); // 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 15.01 { 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("Edit Plugins",Mwin,Badd,Bremove,Bdone,null); zdialog_add_widget(zd,"hbox","hbm","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labm","hbm","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","command","space=5"); zdialog_add_widget(zd,"entry","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_run(zd,edit_plugins_event); 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,"escape")) zd->zstat = 4; // escape = cancel 15.07 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_zuserdir()); // 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[200]; 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,199,"%s/plugfile.tif",tempdir); // /.../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; } if (! resource_lock(Fpaintlock)) goto FAIL; // 15.02 PXM_free(E3pxm); // plugin_file >> E3 E3pxm = pxmtemp; resource_unlock(Fpaintlock); 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-15.11.1/debian-control0000644000175000017500000000133612616075370014504 0ustar micomicoPackage: fotoxx Version: 15.11.1 Architecture: amd64 Section: graphics Installed-Size: 8948 Keywords: image, photo, edit, retouch Maintainer: Mike Cornelison Priority: optional Homepage: http://kornelix.com/fotoxx Depends: libc6, binutils, xdg-utils, libimage-exiftool-perl, dcraw Description: Edit photos and manage a large collection. Includes thumbnail browser/navigator, RAW file conversion, a comprehensive set of edit functions working in deep color, edit/copy/paste selected image areas, file versioning, batch operations, batch edit scripts, albums (alternate views), HDR, HDF, stack, panorama, montage, metadata edit and report, image search using any metadata and/or directory/file names. fotoxx-15.11.1/f.albums.cc0000664000175000017500000046235312616075370013713 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** Fotoxx image edit - menu functions for named image collections (albums) m_manage_albums manage image albums conv_albums convert albums when files are moved m_copyto_cache copy clicked file or current file to the file cache file_addtocache add image file to file cache file_removefromcache get image file from cache file_clearcache clear image file cache album_remove remove file from album album_cutfile remove and add to cache album_pastecache add files from cache to album album_pastecurrfile add current file to album album_show show current album m_slideshow display album images in sequence with artsy transitions m_setdesktop set desktop wallpaper image from Fotoxx current image m_cycledesktop cycle desktop wallpaper images from a Fotoxx album run_cycledesktop cycle desktop wallpaper execution function ***************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) #include /**************************************************************************/ // Manage Albums - create, view, edit named albums of images namespace albums { char *current_album = 0; // album file name char *filez; zdialog *zdalbum = 0; GdkDisplay *display; PIXBUF *pixbuf; GdkCursor *cursor; GdkWindow *Ggdkwin; int dragNth; char albums_copy[200]; }; void album_new(); // start a new album void album_selectfiles(); // select files, add to image cache void album_cuttocache(int posn, int addcache); // remove from album, add to image cache void album_pastecache(int posn, int clear); // paste image cache into current album void album_show(); // show updated album (gallery) void m_manage_albums(GtkWidget *, cchar *) { using namespace albums; int manage_albums_dialog_event(zdialog *zd, cchar *event); // manage albumd dialog event func cchar *popupmess1 = ZTX("Right-click album thumbnail to cut/copy \n" "to cache, insert from cache, or remove."); cchar *popupmess2 = ZTX("Drag album thumbnail to new position."); F1_help_topic = "manage_albums"; if (zdalbum) 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 | | [Images] 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. | | Drag album thumbnail to new position. | | | | [done] | |____________________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Manage Albums"),Mwin,Bdone,null); 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","images","vb1",Bimages); 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","hbimages","vb2"); zdialog_add_widget(zd,"label","labimages","hbimages",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","hbpopup1","dialog",0,"space=8"); zdialog_add_widget(zd,"label","labpopup1","hbpopup1",popupmess1,"space=5"); zdialog_add_widget(zd,"hbox","hbpopup2","dialog"); zdialog_add_widget(zd,"label","labpopup2","hbpopup2",popupmess2,"space=5"); zdialog_run(zd,manage_albums_dialog_event,"save"); zdalbum = zd; 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 (strmatch(event,"enter")) zd->zstat = 1; // [done] if (strmatch(event,"escape")) zd->zstat = 1; // escape = done 15.07 if (zd->zstat) { // [done] or [x] zdialog_free(zd); zdalbum = 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 { cfile = zgetfile(choosealbum,MWIN,"file",albums_dirk); // choose album file if (! cfile) return 1; if (current_album) zfree(current_album); // set current album for editing current_album = zstrdup(cfile); zfree(cfile); album_show(); // show album } if (strmatch(event,"images")) // select image files and add to image cache { zdialog_show(zd,0); // hide manage albums dialog album_selectfiles(); // select files, add to image cache if (current_album) { album_show(); // restore album gallery if (navi::nfiles == 0) { // album is empty album_pastecache(0,1); // insert cache into album album_show(); zmessageACK(Mwin,dumpmess); } } zdialog_show(zd,1); // restore manage albums dialog } if (strmatch(event,"clear")) // clear image cache file_clearcache(); 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 (navi::galleryname && strmatch(cfile,navi::galleryname)) // current gallery is deleted { zfree(navi::galleryname); // bugfix 15.07 navi::galleryname = 0; navi::gallerytype = 0; m_viewmode(0,"F"); } if (current_album && strmatch(cfile,current_album)) { zfree(current_album); current_album = 0; } remove(cfile); zfree(cfile); } snprintf(ncachetext,60,ncacheFormat,Nfilecache); // update cache count 15.11 zdialog_stuff(zd,"labNcache",ncachetext); return 1; } // start 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)"),Nfilecache); /*** __________________________________________________ | 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,"entry","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); 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, albumname[100], albumfile[200]; char *pp, *ifile; int Nth, ftype; FILE *fid; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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,100); // get album name if (*albumname <= ' ') { zmessageACK(Mwin,ZTX("enter an album name")); zd->zstat = 0; // keep dialog active return 1; } snprintf(albumfile,200,"%s/%s",albums_dirk,albumname); // make filespec fid = fopen(albumfile,"w"); // open/write empty album file if (! fid) { zmessageACK(Mwin,strerror(errno)); return 1; } fclose(fid); if (current_album) zfree(current_album); // set current album for editing current_album = zstrdup(albumfile); // use heap address 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 { if (navi::nimages == 0) { zmessageACK(Mwin,ZTX("gallery is empty")); return 1; } fid = fopen(current_album,"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 { ifile = gallery(0,"find",Nth); if (! ifile) break; ftype = image_file_type(ifile); // must be image or RAW file if (ftype != 2 && ftype != 3) { zfree(ifile); continue; } fprintf(fid,"%s\n",ifile); zfree(ifile); } fclose(fid); } album_show(); zmessage_post(Mwin,3,ZTX("new album created")); return 1; } // manage albums utility functions void album_selectfiles() // select files, add to cache 15.06 { using namespace albums; int ii; char **selfiles = 0; selfiles = gallery_getfiles(filecache); // get cached files + new files if any if (! selfiles) return; // nothing selected file_clearcache(); // replace file cache filecache = selfiles; for (ii = 0; selfiles[ii]; ii++); // count entries Nfilecache = ii; return; } void album_cuttocache(int posn, int addcache) // remove from album, add to cache { using namespace albums; int ii; char *pp, cachebuff[XFCC]; FILE *fidr, *fidw; if (! current_album) return; snprintf(albums_copy,199,"%s/albums_copy",get_zuserdir()); // albums copy file fidw = fopen(albums_copy,"w"); // open/write copy file if (! fidw) { zmessageACK(Mwin,strerror(errno)); return; } fidr = fopen(current_album,"r"); // open/read album file if (! fidr) { zmessageACK(Mwin,strerror(errno)); fclose(fidw); return; } for (ii = 0; ; ii++) // loop album member files { pp = fgets_trim(cachebuff,XFCC,fidr); // album member file if (! pp) break; if (ii == posn) { // position to remove if (addcache) file_addtocache(pp); // add to image cache if wanted continue; // omit from copy file } fprintf(fidw,"%s\n",pp); // add to copy file } fclose(fidr); fclose(fidw); rename(albums_copy,current_album); // replace album file with copy return; } void album_pastecache(int posn, int clear) // insert cached files into album { // at designated position using namespace albums; int ii, jj; char *pp, cachebuff[XFCC]; FILE *fidr, *fidw; if (! current_album) return; if (Nfilecache == 0) return; snprintf(albums_copy,199,"%s/albums_copy",get_zuserdir()); // albums copy file fidw = fopen(albums_copy,"w"); // open/write copy file if (! fidw) { zmessageACK(Mwin,strerror(errno)); return; } fidr = fopen(current_album,"r"); // open/read album file if (! fidr) { zmessageACK(Mwin,strerror(errno)); fclose(fidw); return; } for (ii = 0; ; ii++) // loop album member files { if (ii == posn) for (jj = 0; jj < Nfilecache; jj++) // add cached files here fprintf(fidw,"%s\n",filecache[jj]); pp = fgets_trim(cachebuff,XFCC,fidr); // copy album member file if (! pp) break; // EOF fprintf(fidw,"%s\n",pp); } fclose(fidr); fclose(fidw); rename(albums_copy,current_album); // replace album file with copy if (clear) file_clearcache(); return; } void album_pastecurrfile(int posn) // insert current file { // at designated position using namespace albums; int ii; char *pp, cachebuff[XFCC]; FILE *fidr, *fidw; if (! current_album) return; if (! curr_file) return; snprintf(albums_copy,199,"%s/albums_copy",get_zuserdir()); // albums copy file fidw = fopen(albums_copy,"w"); // open/write copy file if (! fidw) { zmessageACK(Mwin,strerror(errno)); return; } fidr = fopen(current_album,"r"); // open/read album file if (! fidr) { zmessageACK(Mwin,strerror(errno)); fclose(fidw); return; } for (ii = 0; ; ii++) // loop album member files { if (ii == posn) fprintf(fidw,"%s\n",curr_file); // insert current file here pp = fgets_trim(cachebuff,XFCC,fidr); // copy album member file if (! pp) break; // EOF fprintf(fidw,"%s\n",pp); } fclose(fidr); fclose(fidw); rename(albums_copy,current_album); // replace album file with copy return; } void album_show() // show current album { using namespace albums; int type; char *pp, cachebuff[XFCC]; FILE *fidr, *fidw; if (! current_album) return; snprintf(albums_copy,199,"%s/albums_copy",get_zuserdir()); // albums copy file fidw = fopen(albums_copy,"w"); // open/write copy file if (! fidw) { zmessageACK(Mwin,strerror(errno)); return; } fidr = fopen(current_album,"r"); // open/read album file if (! fidr) { zmessageACK(Mwin,strerror(errno)); fclose(fidw); return; } while (true) // copy album member files { pp = fgets_trim(cachebuff,XFCC,fidr); if (! pp) break; type = image_file_type(pp); // remove deleted files if (type < 2 || type > 3) { printz("remove deleted file from album: \n %s \n",pp); continue; } fprintf(fidw,"%s\n",pp); } fclose(fidr); fclose(fidw); rename(albums_copy,current_album); // replace album file with copy free_resources(); // no current file navi::gallerytype = 4; gallery(current_album,"initF"); m_viewmode(0,"G"); gallery(0,"paint",-1); return; } // manage album drag and drop functions void album_drag_start(int Nth) { using namespace albums; int hpx, hpy; dragNth = -1; if (navi::gallerytype != 4) return; if (current_album) zfree(current_album); // album being edited current_album = zstrdup(navi::galleryname); if (filez) zfree(filez); filez = 0; if (pixbuf) g_object_unref(pixbuf); pixbuf = 0; if (cursor) g_object_unref(cursor); cursor = 0; filez = gallery(0,"find",Nth); if (! filez) return; pixbuf = image_thumbnail(filez,navi::thumbsize/2); if (! pixbuf) return; hpx = gdk_pixbuf_get_width(pixbuf) / 2; hpy = gdk_pixbuf_get_height(pixbuf) / 2; display = gdk_display_get_default(); cursor = gdk_cursor_new_from_pixbuf(display,pixbuf,hpx,hpy); if (! cursor) return; Ggdkwin = gtk_widget_get_window(Gdrawin); gdk_window_set_cursor(Ggdkwin,cursor); dragNth = Nth; return; } void album_drag_drop(int Nth) { using namespace albums; int ii, pos1, pos2; char *pp, buff[XFCC]; FILE *fidr, *fidw; if (! current_album) return; if (dragNth < 0) return; Ggdkwin = gtk_widget_get_window(Gdrawin); gdk_window_set_cursor(Ggdkwin,0); if (pixbuf) g_object_unref(pixbuf); pixbuf = 0; if (cursor) g_object_unref(cursor); cursor = 0; gdk_window_set_cursor(Ggdkwin,arrowcursor); // double cursor problem - does not help pos1 = dragNth; pos2 = Nth; if (pos1 == pos2) return; filez = gallery(0,"find",pos1); if (! filez) return; snprintf(albums_copy,199,"%s/albums_copy",get_zuserdir()); // albums copy file fidw = fopen(albums_copy,"w"); // open/write copy file if (! fidw) { zmessageACK(Mwin,strerror(errno)); return; } fidr = fopen(current_album,"r"); // open/read album file if (! fidr) { zmessageACK(Mwin,strerror(errno)); fclose(fidw); return; } for (ii = 0; ; ii++) // loop album member files { pp = fgets_trim(buff,XFCC,fidr); // album member file if (! pp) break; if (ii == pos1) continue; // skip file position to move from if (ii == pos2) { // file position to move to fprintf(fidw,"%s\n",filez); // insert the moved file here pos2 = -1; } fprintf(fidw,"%s\n",pp); // copy to output file } if (pos2 >= 0) fprintf(fidw,"%s\n",filez); // add at the end fclose(fidr); fclose(fidw); rename(albums_copy,current_album); // replace album file with copy album_show(); return; } // manage album right-click popup menus void album_remove(GtkWidget *, cchar *menu) // remove image from album { using namespace albums; if (! clicked_file) return; if (navi::gallerytype != 4) { zfree(clicked_file); // reset clicked file clicked_file = 0; return; } if (current_album) zfree(current_album); // album being edited current_album = zstrdup(navi::galleryname); album_cuttocache(clicked_posn,0); // remove clicked file zfree(clicked_file); // reset clicked file clicked_file = 0; album_show(); return; } void album_cutfile(GtkWidget *, cchar *menu) // remove image from album, add to cache { using namespace albums; if (! clicked_file) return; if (navi::gallerytype != 4) { zfree(clicked_file); // reset clicked file clicked_file = 0; return; } if (current_album) zfree(current_album); // album being edited current_album = 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; } void album_pastecache(GtkWidget *, cchar *menu) // add cached image files to album { // at clicked position using namespace albums; // clear cache if wanted int posn, clear; if (! clicked_file) return; if (navi::gallerytype != 4) { // clicked file not an album zfree(clicked_file); // reset clicked file clicked_file = 0; return; } if (current_album) zfree(current_album); // album being edited current_album = 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; } void album_pastecurrfile(GtkWidget *, cchar *menu) // add current image file 15.07 { // at clicked position using namespace albums; int posn; if (! clicked_file) return; if (navi::gallerytype != 4) { // clicked file not an album zfree(clicked_file); // reset clicked file clicked_file = 0; return; } if (current_album) zfree(current_album); // album being edited current_album = zstrdup(navi::galleryname); posn = clicked_posn; if (clicked_width > 50) ++posn; // thumbnail right side clicked, bump posn album_pastecurrfile(posn); // paste file at position zfree(clicked_file); // reset clicked file clicked_file = 0; album_show(); // show revised album return; } /**************************************************************************/ // Fix albums when image files have been renamed or moved. // inputs: a list of old filespecs and corresponding new filespecs. // used by batch_convert(). void conv_albums(char **oldfiles, char **newfiles, int Nfiles) { char *pp, *albumnames[999]; char buffr[XFCC], buffw[XFCC]; char albumfile[200], tempfile[200]; int ii, jj, err; int Nalbum, contx; FILE *fidr, *fidw; 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"); fidr = fidw = 0; *tempfile = 0; for (ii = 0; ii < Nalbum; ii++) // loop all albums { strcpy(albumfile,albumnames[ii]); // album file - input strcpy(tempfile,albumfile); // temp file - output strcat(tempfile,".new"); fidr = fopen(albumfile,"r"); if (! fidr) goto error; fidw = fopen(tempfile,"w"); if (! fidw) goto error; while ((pp = fgets_trim(buffr,XFCC,fidr))) // read all album recs = image files { for (jj = 0; jj < Nfiles; jj++) // list of renamed/moved files if (strmatch(buffr,oldfiles[jj])) break; // includes this file? if (jj == Nfiles) strcpy(buffw,buffr); // no, copy old filespec else strcpy(buffw,newfiles[jj]); // yes, copy corresp. new filespec jj = fprintf(fidw,"%s\n",buffw); // write to output file if (jj < 0) goto error; } err = fclose(fidr); err = fclose(fidw); fidr = fidw = 0; if (err) goto error; err = rename(tempfile,albumfile); // replace album with temp file *tempfile = 0; if (err) goto error; } goto cleanup; error: zmessageACK(Mwin,"%s \n %s",albumfile,strerror(errno)); cleanup: if (fidr) fclose(fidr); if (fidw) fclose(fidw); if (*tempfile) remove(tempfile); *tempfile = 0; for (ii = 0; ii < Nalbum; ii++) zfree(albumnames[ii]); Nalbum = 0; return; } /**************************************************************************/ // function to copy an image file to the file cache // used in file view and thumbnail right-click popup menu // uses and frees clicked_file if present, else uses current file void m_copyto_cache(GtkWidget *, cchar *) { if (clicked_file) { file_addtocache(clicked_file); zfree(clicked_file); clicked_file = 0; } else if (curr_file) file_addtocache(curr_file); return; } /**************************************************************************/ // add image file to image cache at end position // returns current cache count int file_addtocache(char *file) { int cc, cc1, cc2; char **newcache; cc = sizeof(char *); cc1 = Nfilecache * cc; // current cache size cc2 = cc1 + cc; // new cache size, 1 more newcache = (char **) zmalloc(cc2); if (cc1) { memcpy(newcache,filecache,cc1); // copy old cache to new cache zfree(filecache); // and free old cache } filecache = newcache; // new bigger cache filecache[Nfilecache] = zstrdup(file); // add new image file at the end Nfilecache++; return Nfilecache; } // return first image file in cache and remove from cache // file is subject for zfree() // returns 0 if cache empty char * file_removefromcache() { int cc, cc1, cc2; char **newcache; char *file; if (! Nfilecache) return 0; // cache empty file = zstrdup(filecache[0]); // get file to return cc = sizeof(char *); cc1 = Nfilecache * cc; // current cache size cc2 = cc1 - cc; // new cache size, 1 less if (cc2) { newcache = (char **) zmalloc(cc2); // new cache, 1 less file than before memcpy(newcache,filecache+cc,cc2); // copy old cache - 1st file to new cache zfree(filecache); // and free old cache filecache = newcache; } else filecache = 0; Nfilecache--; return file; } // clear the image file cache void file_clearcache() { if (Nfilecache) zfree(filecache); filecache = 0; Nfilecache = 0; return; } /**************************************************************************/ // slide show function int slideshow_dialog_event(zdialog *zd, cchar *event); // user dialogs 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 void ss_conv_prefsfile(); // convert prefs file to new format 15.02 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_japfan(); void ss_jaws(); void ss_ellipse(); void ss_raindrops(); void ss_doubledoor(); void ss_rotate(); void ss_fallover(); void ss_sphereoid(); void ss_zoomin(); void ss_zoomout(); char ss_albumfile[300] = ""; // 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 = 1; // 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_ww, ss_hh; // slide show drawing window size char *ss_oldfile, *ss_newfile; // image files for transition PIXBUF *ss_pxbold, *ss_pxbnew; // pixbuf images: old, new int ss_rsold, ss_rsnew; // old and new pixbuf row stride 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_blank = 0; // flag, user pressed B key (blank screen) cairo_t *ss_cr; // cairo context #define SSNF 18 // slide show transition types #define SSMAXI 10000 // max. no. 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 preferences 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 default preference { // name enab slow pref function // (enabled, slowdowns, equal preference) { "instant", 1, 0, 10, ss_instant }, // slowdowns scaled for Intel integrated GPU { "fade-in", 1, 6, 10, ss_fadein }, { "roll-right", 1, 4, 10, ss_rollright }, // NO BLANKS IN TRANSITION NAMES { "roll-down", 1, 4, 10, ss_rolldown }, { "venetian", 1, 2, 10, ss_venetian }, { "grate", 1, 2, 10, ss_grate }, { "rectangle", 1, 2, 10, ss_rectangle }, { "implode", 1, 10, 10, ss_implode }, { "explode", 1, 10, 10, ss_explode }, { "radar", 1, 2, 10, ss_radar }, { "Japanese-fan", 1, 2, 10, ss_japfan }, { "jaws", 1, 4, 10, ss_jaws }, { "ellipse", 1, 10, 10, ss_ellipse }, { "raindrops", 1, 3, 10, ss_raindrops }, { "doubledoor", 1, 12, 10, ss_doubledoor }, { "rotate", 1, 6, 10, ss_rotate }, { "fallover", 1, 5, 10, ss_fallover }, { "sphereoid", 1, 0, 10, ss_sphereoid } }; 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 15.07 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 countmess[50], *pp; cchar *esc_message = ZTX("Press ESC to exit slide show"); ZTX("instant"); // add translations to .po file 15.09 ZTX("fade-in"); ZTX("roll-right"); ZTX("roll-down"); ZTX("venetian"); ZTX("grate"); ZTX("rectangle"); ZTX("implode"); ZTX("explode"); ZTX("radar"); ZTX("Japanese-fan"); ZTX("jaws"); ZTX("ellipse"); ZTX("raindrops"); ZTX("doubledoor"); ZTX("rotate"); ZTX("fallover"); ZTX("sphereoid"); F1_help_topic = "slide_show"; if (checkpend("all")) return; // check nothing pending /*** _____________________________________________________ | [x] [-] [ ] Slide Show | | | | [Select] album-name 123 images | | Seconds [___|-+] Clip Limit (%) [___|-+] | | Music File: /directory/.../filename.ogg [Browse] | | [x] Full Screen [x] Auto-replay | | Press ESC to exit slide show | | Customize: [transitions] [image files] | | | | [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,"hbox","hbprefs","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labsecs","hbprefs",Bseconds,"space=5"); zdialog_add_widget(zd,"spin","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,"spin","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,"entry","musicfile","hbmuf","none","size=40|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=3"); zdialog_add_widget(zd,"hbox","hbesc","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labesc","hbesc",esc_message,"space=20"); 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"); if (ss_Nfiles) { // if album file available, ss_loadprefs(); // load slide show data or defaults zdialog_stuff(zd,"albumname",ss_albumname); 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); // 15.03 zdialog_stuff(zd,"replay",ss_replay); navi::gallerytype = 4; // open gallery with slide show album gallery(ss_albumfile,"initF"); gallery(0,"paint",ss_Fcurrent); m_viewmode(0,"G"); } zdialog_run(zd,slideshow_dialog_event,"save"); // run dialog zstat = zdialog_wait(zd); // wait for completion if (zstat != 1) { // cancel zdialog_free(zd); return; } if (! ss_Nfiles) { // no selection zmessageACK(Mwin,ZTX("invalid album")); zdialog_free(zd); 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; ss_Fcurrent = ii; // next file in list to show f_open(ss_imagetab[ii].imagefile); // 15.11 m_viewmode(0,"F"); // insure tab F if (ss_fullscreen) win_fullscreen(1); // full screen, hide menu and panel ss_newfile = 0; // no new image ss_pxbnew = 0; ss_oldfile = 0; // no old (prior) image ss_pxbold = 0; Fslideshow = 1; // slideshow active for KB events ss_blank = 0; // not blank window 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); g_timeout_add(100,ss_timerfunc,0); 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]; if (strmatch(event,"focus")) F1_help_topic = "slide_show"; if (strmatch(event,"enter")) zd->zstat = 1; // [proceed] if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (zd->zstat == 1) { // [proceed] if (ss_Nfiles) return 1; zmessageACK(Mwin,ZTX("invalid album")); // diagnose and keep dialog open 15.01 zd->zstat = 0; return 1; } if (zd->zstat) return 1; // cancel or [x] 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; } strcpy(ss_albumfile,file); pp = strrchr(ss_albumfile,'/'); // get album name ss_albumname = pp + 1; zfree(file); 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); // 15.03 zdialog_stuff(zd,"replay",ss_replay); navi::gallerytype = 4; // open gallery with slide show album gallery(ss_albumfile,"initF"); m_viewmode(0,"G"); gallery(0,"paint",0); } 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 (! ss_Nfiles) return 1; if (strmatch(event,"transprefs")) ss_transprefs_dialog(); // edit transition preferences if (strmatch(event,"imageprefs")) ss_imageprefs_dialog(); // edit image preferences 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("select random (if 5+ enabled)"); /*** _______________________________________________ | Transition Preferences | | | | [x] select random (if 5+ enabled) | | | | transition enabled slowdown preference | | instant [x] [ 1 ] [ 10 ] | | fade-in [x] [ 2 ] [ 20 ] | | roll-right [x] [ 3 ] [ 0 ] | | .... ... ... ... | | | | [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","hbrand","dialog",0,"space=3"); zdialog_add_widget(zd,"check","rand","hbrand",randmess,"space=3"); 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|homog"); zdialog_add_widget(zd,"vbox","vb3","hb1",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vb4","hb1",0,"space=3|homog"); zdialog_add_widget(zd,"label","labname","vb1",ZTX("transition")); zdialog_add_widget(zd,"label","labenab","vb2",ZTX("enabled")); zdialog_add_widget(zd,"label","labslow","vb3",ZTX("slowdown")); zdialog_add_widget(zd,"label","labpref","vb4",ZTX("preference")); 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); zdialog_add_widget(zd,"label",nameii,"vb1","transition"); zdialog_add_widget(zd,"check",enabii,"vb2",0,"size=3"); zdialog_add_widget(zd,"spin",slowii,"vb3","0|99|1|1","size=3"); zdialog_add_widget(zd,"spin",prefii,"vb4","0|99|1|10","size=3"); zdialog_stuff(zd,nameii,ZTX(ss_trantab[ii].tranname)); // stuff current transition prefs 15.09 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); // 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) { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 return 1; } // 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"; /*** _______________________________________________ | 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 location width [40] height [60] | | 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 15.01 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,"spin","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,"spin","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,"spin","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"); // 15.07 zdialog_add_widget(zd,"label","labztyp","hbztyp",ZTX("Zoom type:"),"space=3"); zdialog_add_widget(zd,"radio","ztyp0","hbztyp",ZTX("none"),"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,"spin","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,"spin","zoomsteps","hbz","50|999|1|200"); zdialog_add_widget(zd,"hbox","hbzloc","dialog"); zdialog_add_widget(zd,"label","labzloc","hbzloc",ZTX("Zoom image location:"),"space=3"); zdialog_add_widget(zd,"label","labzlocx","hbzloc",Bwidth,"space=8"); zdialog_add_widget(zd,"label","zoomlocx","hbzloc"," 50 "); zdialog_add_widget(zd,"label","space","hbzloc",0,"space=5"); zdialog_add_widget(zd,"label","labzlocy","hbzloc",Bheight,"space=8"); zdialog_add_widget(zd,"label","zoomlocy","hbzloc"," 50 "); zdialog_add_widget(zd,"hbox","hbaz","dialog"); zdialog_add_widget(zd,"label","labwait","hbaz",ZTX("Wait after zoom"),"space=3"); zdialog_add_widget(zd,"spin","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 15.09 for (ii = 0; ii < SSNF; ii++) // add all transitions to dropdown list zdialog_cb_app(zd,"tranname",ZTX(ss_trantab[ii].tranname)); // 15.09 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; 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); zdialog_stuff(zd,"zoomlocx",ss_imagetab[ii].zoomlocx); zdialog_stuff(zd,"zoomlocy",ss_imagetab[ii].zoomlocy); zdialog_stuff(zd,"wait2",ss_imagetab[ii].wait2); zdialog_stuff(zd,"tranname",ZTX(ss_imagetab[ii].tranname)); // 15.09 zdialog_run(zd,ss_imageprefs_dialog_event); zdialog_wait(zd); zdialog_free(zd); zd_ss_imageprefs = 0; ss_saveprefs(); 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]; 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 15.07 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,"zoomlocx")) { zdialog_fetch(zd,"zoomlocx",jj); ss_imagetab[ii].zoomlocx = jj; } if (strmatch(event,"zoomlocy")) { zdialog_fetch(zd,"zoomlocy",jj); ss_imagetab[ii].zoomlocy = jj; } 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"))) { // 15.09 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"; if (! clicked_file) return; zfree(clicked_file); clicked_file = 0; zd = zd_ss_imageprefs; if (! zd) return; if (! ss_Nfiles) { zmessageACK(Mwin,ZTX("invalid album")); return; } ii = Nth; if (ii >= ss_Nfiles) return; ss_Fcurrent = ii; ss_imagetab[ii].zoomlocx = clicked_width; // set zoom-in location from ss_imagetab[ii].zoomlocy = clicked_height; // thumbnail click position 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); zdialog_stuff(zd,"zoomlocx",ss_imagetab[ii].zoomlocx); zdialog_stuff(zd,"zoomlocy",ss_imagetab[ii].zoomlocy); zdialog_stuff(zd,"wait2",ss_imagetab[ii].wait2); zdialog_stuff(zd,"tranname",ZTX(ss_imagetab[ii].tranname)); // 15.09 return; } /****************** new preferences file format v.15.02 ***************** 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 15.07 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[300], *pp; int ii, jj, nn, ff, ftype, format; char tranname[32]; int n1, n2, n3; 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 != 2) continue; 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 = 4; // open gallery with slide show album gallery(ss_albumfile,"initF"); m_viewmode(0,"G"); gallery(0,"paint",0); 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 15.07 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,300,"%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,"overall:",8)) { // detect old file format fclose(fid); ss_conv_prefsfile(); // convert to new format 15.02 ss_loadprefs(); // process new format return; } 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); // 15.03 continue; } if (strmatchN(pp,"replay: ",8)) { // random: N 0-1 = no / replay after end ss_replay = atoi(pp+8); // 15.03 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) goto format_error; 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 file 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 15.07 nn = atoi(pp+10); ss_imagetab[ii].zoomtype = nn; continue; } if (strmatchN(pp,"zoomsize: ",10)) { // zoomsize: N.N 1.0 - 3.0 = 3x 15.02 ff = atof(pp+10); 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[300]; int ii; if (! ss_Nfiles) { zmessageACK(Mwin,ZTX("invalid album")); return; } snprintf(prefsfile,300,"%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); // 15.07 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); return; } // convert old preferences file format to new format // remove this code after Jan 2016 FIXME void ss_conv_prefsfile() // 15.02 { FILE *fid1, *fid2; char buff[XFCC]; char old_prefsfile[300], new_prefsfile[300] = ""; char tranname[32], *pp; int nn, cc, format = 0; int n1, n2, n3, n4, n5, n6, n7; snprintf(old_prefsfile,300,"%s/%s",slideshow_dirk,ss_albumname); fid1 = fopen(old_prefsfile,"r"); // open input slide show prefs file if (! fid1) return; strncatv(new_prefsfile,300,old_prefsfile,"-new",0); fid2 = fopen(new_prefsfile,"w"); // open output converted file if (! fid2) { zmessageACK(Mwin,strerror(errno)); fclose(fid1); return; } while (true) { pp = fgets_trim(buff,XFCC,fid1,1); if (! pp) break; if (strmatchN(pp,"overall:",8)) { fprintf(fid2,"global data: \n"); format = 1; continue; } if (strmatchN(pp,"transitions:",12)) { fprintf(fid2,"transitions data: \n"); format = 2; continue; } if (strmatchN(pp,"images:",7)) { fprintf(fid2,"images data: \n"); format = 3; continue; } if (format == 1) { nn = sscanf(buff,"%d %d %d %d \"%m[^\"] ",&n1,&n2,&n3,&n4,&pp); if (nn != 5) goto format_error; fprintf(fid2,"seconds: %d \n",n1); fprintf(fid2,"cliplimit: %d \n",n2); fprintf(fid2,"random: %d \n",n4); fprintf(fid2,"musicfile: %s \n",pp); free(pp); continue; } if (format == 2) { nn = sscanf(buff,"%31s %d %d %d ",tranname,&n1,&n2,&n3); if (nn != 4) goto format_error; fprintf(fid2,"%s %d %d %d \n",tranname,n1,n2,n3); continue; } if (format == 3) { if (pp[0] != '"') goto format_error; // rec. 1 = "/path.../filename" cc = strlen(pp); if (pp[cc-1] != '"') goto format_error; pp[cc-1] = 0; fprintf(fid2,"imagefile: %s \n",pp+1); pp = fgets_trim(buff,XFCC,fid1,1); // rec. 2 = preferences if (! pp) goto format_error; nn = sscanf(buff,"%31s %d %d %d %d %d %d %d ",tranname,&n1,&n2,&n3,&n4,&n5,&n6,&n7); if (nn != 8) goto format_error; fprintf(fid2,"tone: %d \n",n1); fprintf(fid2,"wait1: %d \n",n2); if (n3 > 0) nn = 1; else nn = 0; // zoomtype 15.07 fprintf(fid2,"zoomtype: %d \n",nn); fprintf(fid2,"zoomsize: %.1f \n",(n3+100)/100.0); // conv 0-200 % to 1-3 x fprintf(fid2,"zoomsteps: %d \n",n4); fprintf(fid2,"zoomloc: %d %d \n",n5,n6); fprintf(fid2,"wait2: %d \n",n7); fprintf(fid2,"tranname: %s \n",tranname); continue; } } fclose(fid1); fclose(fid2); rename(new_prefsfile,old_prefsfile); return; format_error: zmessageACK(Mwin,ZTX("file format error: \n %s"),buff); fclose(fid1); fclose(fid2); 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 *) // overhauled 15.02 { int ii, jj; int capsecs, commsecs, mode; char *file; double cctime; static int pmode; static double starttime; float zoom; int zoomtype; ii = ss_Fcurrent; // current image file in album if (ss_escape) { // end slide show mode 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 15.02 win_unfullscreen(); // restore old window size, menu etc. ss_fullscreen = 0; Fslideshow = 0; // reset flags Fblowup = 0; ss_escape = 0; file = ss_imagetab[ii].imagefile; // last file shown --> current file 15.04 f_open(file); m_slideshow(0,0); // return to slide show dialog return 0; // stops the timer } if (ss_spacebar) { // pause/resume ss_spacebar = 0; ss_blank = 0; if (! strmatch(ss_state,"pause")) // pause ss_state = "pause"; 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_Mkey) { // KB M key ss_Mkey = 0; if (! strmatch(ss_state,"pause")) return 1; // ignore if not paused f_open(ss_imagetab[ii].imagefile); // start magnify function m_magnify(0,0); return 1; } if (ss_Bkey) { // blank/unblank screen ss_Bkey = 0; ss_blank = 1 - ss_blank; if (ss_blank) { ss_blankwindow(); ss_state = "pause"; } else ss_state = "instant"; return 1; } if (ss_Larrow) { // left arrow, back one image ss_Larrow = 0; ss_blank = 0; ss_Flast = ss_Fcurrent; ss_Fcurrent--; if (ss_Fcurrent < 0) ss_Fcurrent = ss_Nfiles-1; ss_state = "arrow"; return 1; } if (ss_Rarrow) { // right arrow, forward one image ss_Rarrow = 0; ss_blank = 0; ss_Flast = ss_Fcurrent; ss_Fcurrent++; if (ss_Fcurrent == ss_Nfiles) ss_Fcurrent = 0; ss_state = "arrow"; return 1; } if (strmatch(ss_state,"pause")) return 1; // do nothing if (strmatch(ss_state,"first")) // first image ss_state = "instant"; if (strstr("show 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; // old pixbuf = new ss_rsold = ss_rsnew; ss_newfile = ss_imagetab[ii].imagefile; // new current file ss_pxbnew = ss_loadpxb(ss_newfile); // new pixbuf if (! ss_pxbnew) return 0; zoom = 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 (zoom > 1 && zoomtype == 2) { // next image will be zoomed out 15.07 g_object_unref(ss_pxbnew); ss_zoom_posn(ss_newfile,1,zoom,0); // initial image = zoomed image ss_pxbnew = ss_zoom_posn(0,2,zoom,0); ss_zoom_posn(0,3,0,0); } ss_rsnew = gdk_pixbuf_get_rowstride(ss_pxbnew); 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_trantab[jj].func(); // call transition function } if (ss_fullscreen) { // no title bar, use top panel 15.11 file = strrchr(ss_newfile,'/') + 1; strncpy0(paneltext,file,200); } else gtk_window_set_title(MWIN,ss_newfile); // file name in window title bar if (ss_imagetab[ii].tone) // play tone if specified 15.02 shell_quiet("paplay %s/slideshow-tone.oga &",slideshow_dirk); if (strmatch(ss_state,"arrow")) { ss_state = "pause"; return 1; } ss_state = "wait1"; // wait before zoom-in 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 15.02 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-in 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) { if (ss_imagetab[ii].zoomtype == 1) ss_zoomin(); // zoomin or zoomout 15.07 if (ss_imagetab[ii].zoomtype == 2) ss_zoomout(); } ss_state = "wait2"; // wait for next image ss_timer = get_seconds() + ss_imagetab[ii].wait2; 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; ss_Fcurrent++; if (ss_Fcurrent == ss_Nfiles) ss_Fcurrent = 0; // reached end, back to first image if (ss_Fcurrent == 0 && ! ss_replay) ss_escape = 1; // exit if no auto-replay 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 < 5 || 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) // 15.02 { 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 ss_cr = gdk_cairo_create(gdkwin); // (restore image) pxbclear = gdk_pixbuf_new_subpixbuf(ss_pxbnew,0,0,plww+10,plhh+10); gdk_cairo_set_source_pixbuf(ss_cr,pxbclear,0,0); cairo_paint(ss_cr); cairo_destroy(ss_cr); gtk_main_iteration_do(0); g_object_unref(pxbclear); plww = 0; } 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 15.01.1 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(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,&plww,&plhh); ss_cr = gdk_cairo_create(gdkwin); cairo_set_line_width(ss_cr,1); cairo_set_source_rgb(ss_cr,1,1,1); // draw white background cairo_rectangle(ss_cr,10,10,plww,plhh); cairo_fill(ss_cr); cairo_move_to(ss_cr,10,10); // draw layout with text cairo_set_source_rgb(ss_cr,0,0,0); pango_cairo_show_layout(ss_cr,pangolayout); cairo_destroy(ss_cr); 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; 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); // 15.08 if (ss_fullscreen) { ss_ww = screenww; // drawing window size ss_hh = screenhh; } else { ss_ww = Dww; // drawing window size ss_hh = Dhh; } pxbin = gdk_pixbuf_new_from_file(file,&gerror); // load image file into pixbuf if (! pxbin) { zmessageACK(Mwin,"%s",gerror->message); return 0; } pxbtemp = gdk_pixbuf_stripalpha(pxbin); // stip alpha channel if present 15.08 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 = gdk_pixbuf_scale_simple(pxbin,ww1,hh1,BILINEAR); // rescale image 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 15.01 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); return pxbout; } // write black to entire window void ss_blankwindow() { GdkRGBA GDKdark; GDKdark.red = GDKdark.green = GDKdark.blue = 0.2; GDKdark.alpha = 1; ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_rgba(ss_cr,&GDKdark); cairo_paint(ss_cr); cairo_destroy(ss_cr); gtk_main_iteration_do(0); return; } // instant transition (also used for keyboard arrow keys) void ss_instant() { ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,ss_pxbnew,0,0); cairo_paint(ss_cr); cairo_destroy(ss_cr); gtk_main_iteration_do(0); 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 15.07 if (iinc < 1) iinc = 1; 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_rsold + px * 3; pix2 = pixels2 + py * ss_rsnew + 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]; } ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,pxbmix,0,0); cairo_paint(ss_cr); cairo_destroy(ss_cr); gtk_main_iteration_do(0); } } g_object_unref(pxbmix); 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); 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_rsnew,0,0); ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,px,0); cairo_paint(ss_cr); cairo_destroy(ss_cr); g_object_unref(pixbuf); gtk_main_iteration_do(0); zsleep(delay); } 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); for (py = 0; py < ss_hh-2; py += 4) // 4-deep { pix3 = pixels + py * ss_rsnew; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ss_ww,4,ss_rsnew,0,0); ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,0,py); cairo_paint(ss_cr); cairo_destroy(ss_cr); g_object_unref(pixbuf); gtk_main_iteration_do(0); zsleep(delay); } 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 / 2.0); pixels = gdk_pixbuf_get_pixels(ss_pxbnew); for (py1 = 0; py1 < louversize; py1++) // y-row within each louver { ss_cr = gdk_cairo_create(gdkwin); for (louver = 0; louver < Nlouvers; louver++) // louver, first to last { py2 = py1 + louver * louversize; if (py2 >= ss_hh) break; pix3 = pixels + py2 * ss_rsnew; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ss_ww,1,ss_rsnew,0,0); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,0,py2); cairo_paint(ss_cr); g_object_unref(pixbuf); } cairo_destroy(ss_cr); gtk_main_iteration_do(0); zsleep(delay); } 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 / 2.0); for (py1 = 0; py1 < boxhh; py1++) { ss_cr = gdk_cairo_create(gdkwin); for (row = 0; row < Nrow; row++) { py2 = py1 + row * boxhh; if (py2 >= ss_hh) break; pix3 = pixels + py2 * ss_rsnew; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ss_ww,1,ss_rsnew,0,0); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,0,py2); cairo_paint(ss_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_rsnew,0,0); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,px2,0); cairo_paint(ss_cr); g_object_unref(pixbuf); } cairo_destroy(ss_cr); gtk_main_iteration_do(0); zsleep(delay); } return; } // A rectangle opens up from the center and expands outward 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 / 2.0); pixels = gdk_pixbuf_get_pixels(ss_pxbnew); 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; ss_cr = gdk_cairo_create(gdkwin); pix3 = pixels + py1 * ss_rsnew + px1 * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ww1+1,hh2+1,ss_rsnew,0,0); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,px1,py1); cairo_paint(ss_cr); g_object_unref(pixbuf); pix3 = pixels + py2 * ss_rsnew + px2 * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ww2+1,hh1+1,ss_rsnew,0,0); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,px2,py2); cairo_paint(ss_cr); g_object_unref(pixbuf); pix3 = pixels + py3 * ss_rsnew + px3 * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ww1+1,hh2+1,ss_rsnew,0,0); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,px3,py3); cairo_paint(ss_cr); g_object_unref(pixbuf); pix3 = pixels + py1 * ss_rsnew + px1 * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ww2+1,hh1+1,ss_rsnew,0,0); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,px1,py1); cairo_paint(ss_cr); g_object_unref(pixbuf); cairo_destroy(ss_cr); gtk_main_iteration_do(0); zsleep(delay); } ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,ss_pxbnew,0,0); cairo_paint(ss_cr); cairo_destroy(ss_cr); return; } // old image shrinks to the center, revealing new image void ss_implode() // 15.03 { int Nsteps = 100 + 30 * ss_slowdown; int ww, hh, px, py; float size = 1.0, F; PIXBUF *pxbnew, *pxbold; F = 1.0 * (Nsteps-1) / Nsteps; while (true) { pxbnew = gdk_pixbuf_copy(ss_pxbnew); // new image at full size size = F * pow(size,1.02); if (size < 0.02) 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 ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,pxbnew,0,0); // paint new image cairo_paint(ss_cr); cairo_destroy(ss_cr); gtk_main_iteration_do(0); g_object_unref(pxbnew); g_object_unref(pxbold); } ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,ss_pxbnew,0,0); cairo_paint(ss_cr); cairo_destroy(ss_cr); return; } // new image grows from the center, covering old image void ss_explode() // 15.04 { int Nsteps = 60 + 20 * ss_slowdown; int ww, hh, px, py; float size = 0.05, F; PIXBUF *pxbnew, *pxbold; F = 1.0 * (Nsteps+1) / Nsteps; while (true) { pxbold = gdk_pixbuf_copy(ss_pxbold); // old image at full size size = F * pow(size,0.995); 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 ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,pxbold,0,0); // paint new image cairo_paint(ss_cr); cairo_destroy(ss_cr); gtk_main_iteration_do(0); g_object_unref(pxbnew); g_object_unref(pxbold); } ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,ss_pxbnew,0,0); cairo_paint(ss_cr); cairo_destroy(ss_cr); return; } // New image sweeps into view like a circular radar image void ss_radar() // 15.04 { 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 if (! resource_lock(Fpaintlock)) return; gdk_window_freeze_updates(gdkwin); 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 = 100; // line segment length dT = 1.0 / Rmax; // angle step for (T = 0; T < 2*PI; T += dT) // angle from 0 to 360 deg. { cosT = cosf(T); sinT = sinf(T); ss_cr = gdk_cairo_create(gdkwin); 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_rsold + px * 3; // copy new image pixel to old image pix3 = pixels3 + py * ss_rsnew + 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(ss_cr,pixbuf,px,py); cairo_paint(ss_cr); g_object_unref(pixbuf); } cairo_destroy(ss_cr); gtk_main_iteration_do(0); zsleep(0.0001 * ss_slowdown); } resource_unlock(Fpaintlock); gdk_window_thaw_updates(gdkwin); return; } // new image opens up like a Japanese fan void ss_japfan() // 15.04 { 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 if (! resource_lock(Fpaintlock)) return; gdk_window_freeze_updates(gdkwin); 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 = 100; // line segment length dT = 1.0 / Rmax; // angle step for (T = PI/2; T > -PI/2; T -= dT) // angle from +90 to -90 deg. { cosT = cosf(T); sinT = sinf(T); ss_cr = gdk_cairo_create(gdkwin); 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_rsold + px * 3; // copy new image pixel to old image pix3 = pixels3 + py * ss_rsnew + px * 3; // right side memcpy(pix1,pix3,6); pxL = ss_ww-1 - px; pix1 = pixels1 + py * ss_rsold + pxL * 3; // left side pixel pix3 = pixels3 + py * ss_rsnew + 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(ss_cr,pixbuf,px,py); cairo_paint(ss_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(ss_cr,pixbuf,pxL,py); cairo_paint(ss_cr); g_object_unref(pixbuf); } cairo_destroy(ss_cr); gtk_main_iteration_do(0); zsleep(0.0001 * ss_slowdown); } resource_unlock(Fpaintlock); gdk_window_thaw_updates(gdkwin); 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; ss_cr = gdk_cairo_create(gdkwin); 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; 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_rsnew + px * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ww2,Np,ss_rsnew,0,0); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,px,py); cairo_paint(ss_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_rsnew + px * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ww2,Np,ss_rsnew,0,0); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,px,py); cairo_paint(ss_cr); g_object_unref(pixbuf); } } } zsleep(delay); // 15.04 } gdk_cairo_set_source_pixbuf(ss_cr,ss_pxbnew,0,0); cairo_paint(ss_cr); cairo_destroy(ss_cr); 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); 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; ss_cr = gdk_cairo_create(gdkwin); 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_rsnew + px1 * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ww,3,ss_rsnew,0,0); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,px1,py1); cairo_paint(ss_cr); g_object_unref(pixbuf); } cairo_destroy(ss_cr); gtk_main_iteration_do(0); zsleep(delay); } ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,ss_pxbnew,0,0); cairo_paint(ss_cr); cairo_destroy(ss_cr); 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_new(GDKRGB,0,8,ss_ww,ss_hh); // destination image pixels3 = gdk_pixbuf_get_pixels(pxbmix); rsmix = gdk_pixbuf_get_rowstride(pxbmix); memset(pixels3,0,ss_hh * rsmix); // clear dest. to black ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,pxbmix,0,0); // paint black window cairo_paint(ss_cr); cairo_destroy(ss_cr); Rmin = ss_ww * 0.01; // drop size range Rmax = ss_ww * 0.02; Ndrops = 3000; 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_rsnew + px * 3; pix3 = pixels3 + py * rsmix + px * 3; memcpy(pix3,pix2,3); } pxbdrop = gdk_pixbuf_new_subpixbuf(pxbmix,px1,py1,px2-px1,py2-py1); ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,pxbdrop,px1,py1); cairo_paint(ss_cr); cairo_destroy(ss_cr); g_object_unref(pxbdrop); dtime = 0.001 * (1.0 - pow(1.0*ii/Ndrops,0.1)); dtime = dtime * (1.0 + ss_slowdown); zsleep(dtime); gtk_main_iteration_do(0); } ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,ss_pxbnew,0,0); // fill bits that are still missing cairo_paint(ss_cr); cairo_destroy(ss_cr); g_object_unref(pxbmix); 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_rsnew,0,0) PIXBUF *pixbuf; int bx, px; uint8 *pixels, *pix3; float delay = 0.2 / ss_ww; delay = delay * (1 + ss_slowdown / 2.0); pixels = gdk_pixbuf_get_pixels(ss_pxbnew); for (bx = 0; bx < ss_ww/2; bx++) // bx = 0 ... ww/2 { ss_cr = gdk_cairo_create(gdkwin); 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(ss_cr,pixbuf,px,0); cairo_paint(ss_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(ss_cr,pixbuf,px,0); cairo_paint(ss_cr); g_object_unref(pixbuf); cairo_destroy(ss_cr); gtk_main_iteration_do(0); zsleep(delay); } 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 = 40; 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 / 2.0); if (! resource_lock(Fpaintlock)) return; // 15.02 gdk_window_freeze_updates(gdkwin); 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 < NWT; ii++) { // start worker threads tbusy[ii] = 1; start_detached_thread(ss_rotate_thread1,&Nval[ii]); } for (ii = 0; ii < NWT; ii++) while(tbusy[ii]) zsleep(0.001); ss_cr = gdk_cairo_create(gdkwin); // create and destroy each paint 15.02 gdk_cairo_set_source_pixbuf(ss_cr,pxbmix,0,0); cairo_paint(ss_cr); cairo_destroy(ss_cr); gtk_main_iteration_do(0); } 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 < NWT; ii++) { // start worker threads tbusy[ii] = 1; start_detached_thread(ss_rotate_thread2,&Nval[ii]); } for (ii = 0; ii < NWT; ii++) while(tbusy[ii]) zsleep(0.001); ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,pxbmix,0,0); cairo_paint(ss_cr); cairo_destroy(ss_cr); gtk_main_iteration_do(0); } g_object_unref(pxbmix); resource_unlock(Fpaintlock); gdk_window_thaw_updates(gdkwin); 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 + index; px < cx2; px += NWT) { 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_rsold + vpx * 3; pix3 = pixels3 + py * rsmix + px * 3; memcpy(pix3,pix1,3); } } 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 + index; px < cx1; px += NWT) { 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_rsnew + vpx * 3; pix3 = pixels3 + py * rsmix + px * 3; memcpy(pix3,pix2,3); } } 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() // 15.06 { 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; if (! resource_lock(Fpaintlock)) return; gdk_window_freeze_updates(gdkwin); 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 < NWT; ii++) { // start worker threads tbusy[ii] = 1; // make combined image start_detached_thread(ss_fallover_thread,&Nval[ii]); } for (ii = 0; ii < NWT; ii++) // wait for completion while(tbusy[ii]) zsleep(0.01); ss_cr = gdk_cairo_create(gdkwin); // create and destroy each paint gdk_cairo_set_source_pixbuf(ss_cr,pxbmix,0,0); cairo_paint(ss_cr); cairo_destroy(ss_cr); gtk_main_iteration_do(0); } g_object_unref(pxbmix); resource_unlock(Fpaintlock); gdk_window_thaw_updates(gdkwin); 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 += NWT) // py = 0 ... ss_hh { px = 0; // px = 0 ... ss_ww, new image npix = ss_ww; pix1 = pixels2 + py * ss_rsnew + 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_rsold + 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_sphereoid_names { float Cx, Cy, D, R, F; PIXBUF *pxbold, *pxbnew; uint8 *pixels3; PXB *PXBold; int tbusy[max_threads]; } void ss_sphereoid() // 15.11 { using namespace ss_sphereoid_names; void * ss_sphereoid_thread(void *arg); int ii; 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.95, 0.97, 0.98, 0.98, 0.98, 0.98, // D reduction factor 0.97, 0.96, 0.94, 0.91, 0.86, 0.80, 0.70 }; pxbold = gdk_pixbuf_copy(ss_pxbold); // old image at full size PXBold = PXB_make(pxbold); Cx = ss_ww / 2; // center of sphere Cy = ss_hh / 2; F = 1.0; 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.02 * 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 < NWT; ii++) { // start worker threads tbusy[ii] = 1; // make combined image start_detached_thread(ss_sphereoid_thread,&Nval[ii]); } for (int ii = 0; ii < NWT; ii++) // wait for completion while(tbusy[ii]) zsleep(0.001); ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,pxbnew,0,0); // paint new image cairo_paint(ss_cr); cairo_destroy(ss_cr); gtk_main_iteration_do(0); g_object_unref(pxbnew); zsleep(0.001); } PXB_free(PXBold); ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,ss_pxbnew,0,0); cairo_paint(ss_cr); cairo_destroy(ss_cr); return; } void * ss_sphereoid_thread(void *arg) { using namespace ss_sphereoid_names; int index = *((int *) arg); int px3, py3, dx, dy, vstat; uint8 *pix3, vpix[4]; float px1, py1, s1, s2, T; for (py3 = index; py3 < ss_hh; py3 += NWT) // loop output pixels for (px3 = 0; px3 < ss_ww; px3++) { dx = px3 - Cx; 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; vstat = vpixel(PXBold,px1,py1,vpix); if (! vstat) continue; pix3 = pixels3 + py3 * ss_rsnew + px3 * 3; // input v.pixel >> output pixel memcpy(pix3,vpix,3); } tbusy[index] = 0; pthread_exit(0); return 0; } /**************************************************************************/ // 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 // 3 = release resources // fast: 1 = fast zoom, 0 = slow zoom, highest quality PIXBUF * ss_zoom_posn(char *file, int mode, float zoom, int fast) // 15.07 { 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) { 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 15.08 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 = gdk_pixbuf_scale_simple(pxb1,ww1,hh1,BILINEAR); // rescaled image, 1x 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) { 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_zoom() %d %d %d %d",ox4,oy4,ww3,hh3); if (fast) // fast rescale, less quality pxb3 = pixbuf_rescale_fast(pxb2,ss_ww,ss_hh); else pxb3 = gdk_pixbuf_scale_simple(pxb2,ss_ww,ss_hh,BILINEAR); // scale to window size, high quality g_object_unref(pxb2); pxb2 = 0; return pxb3; } if (mode == 3) // release resources { if (pxb1) g_object_unref(pxb1); if (pxb2) g_object_unref(pxb2); pxb1 = pxb2 = 0; return 0; } return 0; } // slowly zoom-in on the image (Ken Burns effect) void ss_zoomin() // 15.07 { PIXBUF *pixbuf; float zoom; float zoominc = (ss_zoomsize - 1.0) / ss_zoomsteps; // zoom increment if (! resource_lock(Fpaintlock)) return; // block Fpaint() gdk_window_freeze_updates(gdkwin); // stop auto paint ss_zoom_posn(ss_newfile,1,ss_zoomsize,0); // initialize for new image 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 ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,0,0); // paint cairo_paint(ss_cr); cairo_destroy(ss_cr); gtk_main_iteration_do(0); g_object_unref(pixbuf); if (ss_escape) break; } zoom = ss_zoomsize; pixbuf = ss_zoom_posn(0,2,zoom,0); // last image, high quality rescale ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,0,0); // paint cairo_paint(ss_cr); cairo_destroy(ss_cr); gtk_main_iteration_do(0); ss_zoom_posn(0,3,0,0); // free resources resource_unlock(Fpaintlock); gdk_window_thaw_updates(gdkwin); 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() // 15.07 { PIXBUF *pixbuf; float zoom; float zoominc = (ss_zoomsize - 1.0) / ss_zoomsteps; // zoom increment if (! resource_lock(Fpaintlock)) return; // block Fpaint() gdk_window_freeze_updates(gdkwin); // stop auto paint ss_zoom_posn(ss_newfile,1,ss_zoomsize,0); // initialize for new image 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 ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,0,0); // paint cairo_paint(ss_cr); cairo_destroy(ss_cr); gtk_main_iteration_do(0); g_object_unref(pixbuf); if (ss_escape) break; } zoom = 1.0; pixbuf = ss_zoom_posn(0,2,zoom,0); // last image, high quality rescale ss_cr = gdk_cairo_create(gdkwin); gdk_cairo_set_source_pixbuf(ss_cr,pixbuf,0,0); // paint cairo_paint(ss_cr); cairo_destroy(ss_cr); gtk_main_iteration_do(0); ss_zoom_posn(0,3,0,0); // free resources resource_unlock(Fpaintlock); gdk_window_thaw_updates(gdkwin); g_object_unref(ss_pxbnew); // retain final zoomed image ss_pxbnew = pixbuf; // for next transition return; } /**************************************************************************/ // set desktop wallpaper from the current fotoxx image void m_setdesktop(GtkWidget *, cchar *) // 15.06 { cchar *setbg = "gsettings set org.gnome.desktop.background picture-uri"; F1_help_topic = "set_desktop"; if (! curr_file) return; snprintf(command,CCC,"%s \'file://%s\'",setbg,curr_file); // gsettings ... 'file:///.../filename.jpg' shell_quiet(command); return; } /**************************************************************************/ // Cycle desktop wallpaper images from a Fotoxx album. // Runs in background without a window or GUI. // Command line: $ fotoxx -cycledesktop album seconds & // To stop the process: $ killall fotoxx void m_cycledesktop(GtkWidget*, cchar *menu) // 15.06 { F1_help_topic = "cycle_desktop"; showz_userguide("cycle_desktop"); return; } namespace cycledesktop_names { volatile int Nfiles = 0; // file count char **filelist = 0; // file list from album } // called from main() at Fotoxx startup void run_cycledesktop(cchar *album, cchar *aseconds) // 15.06 { using namespace cycledesktop_names; int cycledesktop_timerfunc(void *); char albumfile[300], *file; char buff[XFCC]; int ii, ftype, seconds; FILE *fid; snprintf(albumfile,300,"%s/%s",albums_dirk,album); fid = fopen(albumfile,"r"); // open album file if (! fid) { printz("bad album name \n"); exit(1); } seconds = atoi(aseconds); if (seconds < 10) { printz("seconds must be at least 10 \n"); exit(1); } while (true) { // read all image file names file = fgets_trim(buff,XFCC,fid); if (! file) break; ftype = image_file_type(file); // screen out deleted files if (ftype != 2) continue; Nfiles++; // count the files } fclose(fid); if (! Nfiles) { printz("empty album \n"); exit(1); } filelist = (char **) zmalloc(Nfiles * sizeof(char *)); fid = fopen(albumfile,"r"); // open album file again ii = 0; while (ii < Nfiles) { // read all image file names file = fgets_trim(buff,XFCC,fid); if (! file) break; ftype = image_file_type(file); // screen out deleted files if (ftype != 2) continue; filelist[ii] = zstrdup(file); // add to file list ii++; } fclose(fid); if (ii != Nfiles) zappcrash("ii: %d Nfiles: %d",ii,Nfiles); if (! cycledesktop_album || ! strmatch(album,cycledesktop_album)) // new album cycledesktop_index = 0; // reset file index if (cycledesktop_album) zfree(cycledesktop_album); // set album name cycledesktop_album = zstrdup(album); save_params(); // save parameters, album name and index cycledesktop_timerfunc(0); // first interval, immediate g_timeout_add_seconds(seconds,cycledesktop_timerfunc,0); // call every time interval return; } // timer function, called at intervals of "seconds" int cycledesktop_timerfunc(void *) { using namespace cycledesktop_names; cchar *setbg = "gsettings set org.gnome.desktop.background picture-uri"; char *file; int err; load_params(); // load parameters if (cycledesktop_index >= Nfiles) cycledesktop_index = 0; // last image file, back to first file = filelist[cycledesktop_index]; // next image file: /.../filename.jpg cycledesktop_index++; // update index save_params(); // save parameters snprintf(command,CCC,"%s \'file://%s\'",setbg,file); // gsettings ... 'file:///.../filename.jpg' err = shell_quiet(command); if (err) exit(1); return 1; } fotoxx-15.11.1/zfuncs.cc0000644000175000017500000146161112616075370013507 0ustar micomico/************************************************************************** zfuncs.cpp collection of Linux and GDK/GTK utility functions Copyright 2006-2015 Michael Cornelison source code URL: http://kornelix.com 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . These programs originate from the author's web site: http://kornelix.com Other web sites may offer them for download. Modifications could have been made. If you have questions, suggestions or a bug to report: kornelix@posteo.de ***************************************************************************/ // zfuncs.cpp version v.6.2 #include "zfuncs.h" /************************************************************************** Table of Contents ================= System Utility Functions ------------------------ zmalloc zfree zstrdup replace malloc() etc. to add 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 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 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 comand_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 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 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) SearchWildIgnoreCase works like SearchWild() but ignores case in file name 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) 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 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 zmake_menu_launcher create desktop icon / launcher ZTX(), etc. translate GUI and message text strings for non-English locales GTK Utility Functions --------------------- zmainloop do a main loop to process menu events, etc. zthreadcrash crash if called from a thread other than main() get_hardware_info get hardware information (display, screen, mouse, DPI) move_pointer move the mouse pointer within a widget/window wprintx etc. printf() to window at specified row or next row wscroll scroll text window to put line on screen wclear clear a wprintf window wscanf read text from edit window, line at a time wfiledump dump text window to a file wfilesave wfiledump with save-as dialog wprintp print text window to default printer textwidget funcs intercept mouse clicks on text windows, get clicked text menus / toolbars simplified GTK menus, toolbars and status bars popup menus create popup menus gmenuz customizable graphic popup menu Vmenu vertical menu/toolbar in vertical packing box zdialog, etc. simplified GTK dialogs write_popup_text write rows of text to a popup window popup_command run a shell command with output in a popup window zmessageACK popup message, printf format, wait for user ACK zmessLogACK same with parallel output to STDOUT zmessageYN popup message, printf format, wait for user Yes / No zmessage_post popup message, printf format, show until killed zdialog_text popup dialog to get 1-2 lines of text input from user zdialog_choose popup dialog 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 dialog print_image_file dialog to print an image file using GTK functions drag_drop_connect connect window drag-drop event to user function 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 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 { GdkDisplay *display; // X workstation (KB, mouse, screen) GdkDeviceManager *manager; // knows screen / mouse associations GdkScreen *screen; // monitor GdkDevice *mouse; // pointer device GtkSettings *settings; // screen settings 6.2 char zappname[20]; // app name/version char zprefix[200], zdatadir[200], zdocdir[200]; // app directories char zicondir[200], zlocalesdir[200], zuserdir[200]; char zlang[8] = "en"; // "lc" or "lc_RC" char JPGquality[4] = "90"; // JPG file save quality 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 menu icon click posn. 0-100 int vmenuclickbutton; // "" button: 1/2/3 = L/M/R mouse #define zdialog_max 20 // max. parallel zdialogs 6.0 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) } /************************************************************************** system-level utility functions ***************************************************************************/ // replacements for malloc(), free(), and strdup() with call counters // 5.8 void *zmalloc(uint cc) { static uint allocated = 0, fwarn = 0; double free, cache; ///printz("zmalloc %p %d \n",__builtin_return_address(0),cc); allocated += cc; // check memory each 1 MB allocated if (allocated > 1000000) { allocated = 0; parseprocfile("/proc/meminfo","MemFree:",&free,"Cached:",&cache,0); free = (free + cache) / 1024.0; if (free < 500 && ! fwarn) { // warn if < 500 MB 6.0 fwarn = 1; printz("LOW MEMORY - performance may be poor \n"); zpopup_message(10," LOW MEMORY - performance may be poor"); } } zfuncs::Nmalloc++; void *pp = malloc(cc); if (pp) { memset(pp,0,cc); // force real memory allocation 5.9 return pp; } cc = cc >> 10; zappcrash("malloc(%u KB) failure, OUT OF MEMORY",cc); exit(12); } void zfree(void *pp) { ///printz("zfree %p \n",__builtin_return_address(0)); int *ppp = (int *) pp; // clobber released memory 5.9 *ppp = -1; zfuncs::Nfree++; free(pp); return; } char *zstrdup(cchar *string, int addcc) { ///printz("zstrdup %p \n",__builtin_return_address(0)); zfuncs::Nstrdup++; char *pp = (char *) zmalloc(strlen(string) + 1 + addcc); // bugfix 6.0 strcpy(pp,string); return pp; } void zmalloc_report() { using namespace zfuncs; static double ptime = 0; struct mallinfo memstats; uint32 totmem; cchar *format = " process time: %.2f \n" " zdialog: %d open: %d \n" " zmalloc: %d zstrdup: %d zfree: %d MB: %d \n"; ptime = jobtime() - ptime; // process time - prior value memstats = mallinfo(); totmem = memstats.uordblks / 1000000; printz(format, ptime, zdialog_count, zdialog_busy, Nmalloc, Nstrdup, Nfree, totmem); ptime = jobtime(); // reset some counters Nmalloc = Nstrdup = Nfree = 0; return; } /**************************************************************************/ // 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]; int err; 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 file if (secs > 0) { // 6.0 snprintf(timeout,20,"-timeout %d ",secs); strcat(command,timeout); } strcat(command,message); strcat(command," &"); // return immediately err = system(command); if (err) return; // stop compiler warning return; } /**************************************************************************/ // produce a backtrace dump to stdout void zbacktrace() // 5.9 { using namespace zfuncs; 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 { using namespace zfuncs; 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 message[300], progexe[300]; char buff1[300], buff2[300], hexaddr[20]; char *arch, *pp1, *pp2, *pfunc; if (crash++) return; // re-entry or multiple threads crash va_start(arglist,format); vsnprintf(message,300,format,arglist); va_end(arglist); uname(&unbuff); arch = unbuff.machine; printz("*** zappcrash: %s %s %s \n",arch,zappname,message); // output message to stdout zbacktrace(); // backtrace to stdout nstack = backtrace(stacklist,nstack); // get backtrace data if (nstack <= 0) { printz("*** zappcrash backtrace() failure \n"); exit(1); } if (nstack > 100) nstack = 100; fid1 = fopen("backtrace","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("backtrace","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 \n",arch,zappname,message); fprintf(fid2,"*** please send to kornelix@posteo.de *** \n"); printz("\n*** zappcrash: %s %s %s \n",arch,zappname,message); printz("*** 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"); printz("progexe not available \n"); Flinenos = 0; } err = system("which addr2line >> /dev/null"); // check if addr2line() available if (err) { fprintf(fid2,"addr2line not available \n"); printz("addr2line not available \n"); Flinenos = 0; } for (ii = 0; ii < nstack; ii++) // loop backtrace records { fgets_trim(buff1,300,fid1); // read backtrace line if (! Flinenos) goto output; pfunc = 0; pp1 = strstr(buff1,"[0x"); // look for hex address [0x.....] if (! pp1) goto output; pp2 = strchr(pp1,']'); if (! pp2) goto output; *pp2 = 0; strncpy0(hexaddr,pp1+1,20); *pp2 = ']'; snprintf(buff2,300,"addr2line -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 printz("%s \n",buff1); // 6.2 } fclose(fid2); err = system("xdg-open zappcrash"); // popup zappcrash text file if (err) printz("*** xdg-open failure \n"); 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 5.9 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 (bad address)"; if (signal == SIGILL) signame = "illegal operation"; if (signal == SIGFPE) signame = "arithmetic exception"; if (signal == SIGBUS) signame = "bus error (bad address)"; if (signal == SIGABRT) signame = "bug detected"; zappcrash("fatal signal: %s",signame); return; } /**************************************************************************/ // Implement the TRACE macro. // 5.7 // 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; } /**************************************************************************/ // This function will restart the current program with root privileges, // if the correct (sudo) password is given. It does not return. // "argc" and "argv" are passed in the command line. // Use the original argc and argv (optional, may be omitted). // argv[0] is omitted to avoid passing the program name twice. // argv[] parameters are enclosed in quotes to avoid gksu eating them. // bugfix 5.6 void beroot(int argc, char *argv[]) { int cc1, cc2, ii, err; char command[1000]; if (getuid() == 0) return; // already root strcpy(command,"which gksu > /dev/null 2>&1"); // Debian err = system(command); strcpy(command,"gksu \""); if (err) { strcpy(command,"which beesu > /dev/null 2>&1"); // Fedora, just to be different err = system(command); strcpy(command,"beesu \""); } if (err) { printz("*** please install gksu or beesu or restart using sudo \n"); zpopup_message(0,"please install gksu or beesu or restart using sudo",0); exit(0); } cc1 = strlen(command); // gksu (or) beesu cc2 = readlink("/proc/self/exe",command+cc1,990); if (cc2 <= 0) zappcrash("readlink /proc/self/exe: %s",strerror(errno)); // 6.0 command[cc1+cc2] = 0; // gksu or beesu for (ii = 1; ii < argc; ii++) // append command line parameters strncatv(command,990," ",argv[ii],null); strcat(command,"\" &"); // return immediately printz("%s \n",command); err = system(command); exit(0); } /**************************************************************************/ // 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; // 5.2 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) // 5.5 { 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; } /**************************************************************************/ // 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 "sensors" command from package lm-sensors int coretemp() { FILE *fid; char buff[100], *pp; int temp = 0; fid = popen("sensors","r"); if (! fid) return temp; while (true) { pp = fgets(buff,100,fid); if (! pp) break; if (! strmatchN(pp,"Physical id 0:",14)) continue; pp = strchr(pp+14,'+'); if (! pp) break; temp = atoi(pp); break; } pclose(fid); return temp; } /**************************************************************************/ // get current temperature for given disk, e.g. "/dev/sda" int disktemp(char *disk) // 5.9 { 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) // 5.2 { 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); printz("resource locked \n"); // fail, already locked return 0; } 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_t ptid; pthread_attr_t ptattr; int pterr; pthread_attr_init(&ptattr); pthread_attr_setdetachstate(&ptattr,PTHREAD_CREATE_DETACHED); pterr = pthread_create(&ptid,&ptattr,threadfunc,arg); if (pterr) zappcrash("start_detached_thread() failure"); 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 } /**************************************************************************/ // 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, ...) // 5.5 { 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 = system(cbuff); if (! err) { zfree(cbuff); return 0; } err = WEXITSTATUS(err); // special BS for subprocesses 5.8 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) // special case 5.8 printz("%s *** %s \n",cbuff,"command not found"); 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, ...) // 5.5 { 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); // 5.6 err = system(cbuff); if (! err) { zfree(cbuff); return 0; } err = WEXITSTATUS(err); // special BS for subprocesses 5.8 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 5.8 zmessageACK(0,"%s \n %s",cbuff,"command not found"); } else { printz("%s *** %s \n",cbuff,wstrerror(err)); // log error message zmessageACK(0,"%s \n %s",cbuff,wstrerror(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, ...) // 5.5 { 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 = system(command[ii]); // start command, wait until done if (! err) { status[ii] = 0; return 0; } err = WEXITSTATUS(err); // special BS for subprocesses 5.8 if (err == 127) printz("%s \n *** %s \n",command[ii],"command not found"); // special case 5.8 else printz("%s \n *** %s \n",command[ii],wstrerror(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 5.5 { FILE *fid; if (! CO_contx[contx]) return 0; // context already closed 5.8 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 = system(xtcommand); if (err) err = WEXITSTATUS(err); // special BS for subprocesses 5.8 return err; } if (strmatch(sucomm,"su")) { snprintf(xtcommand,499,"xterm -geometry 40x3 -e su -c %s",command); err = system(xtcommand); if (err) err = WEXITSTATUS(err); 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) // 5.9 { 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) // 5.8 { 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; } /************************************************************************** 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; ***************************************************************************/ volatile double cpu_profile_table[100]; volatile double cpu_profile_timer; volatile double cpu_profile_elapsed; volatile 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() // 5.8 { 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 5.2 cchar quote = '"'; int ii, nf, fcc = 0; static char blankstring[2], nullstring[1]; if (! string) return 0; // bad call 5.3 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"); // 5.2 } 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.036 usec for 20 char. string 3.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 & 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 // 5.5 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; // 5.5 } /************************************************************************** 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; 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++; 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) // 5.2 { 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) // 5.2 { 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) // 5.5 { 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 shell find command is used for the initial search because this is much faster than recursive use of readdir() (why?). (#) 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 457 from 101K files 1.9 secs. 3.3 GHz Core i5 SSD disk ***/ 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; // kill flag, done if (uflag == 1) // first call flag { cc = strlen(wpath); if (cc == 0) return 0; if (cc > XFCC-20) zappcrash("SearchWild: wpath > XFCC"); pp = (char *) wpath; repl_Nstrs(pp,searchpath,"$","\\$","\"","\\\"",null); // init. search path, escape $ and " 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 \"%s\" -type f -or -type l",searchpath); // find files (ordinary, symlink) 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 } } /************************************************************************** SearchWildIgnoreCase - 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 * SearchWildIgnoreCase(cchar *wpath, int &uflag) // 5.5 { 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; // kill flag, done if (uflag == 1) // first call flag { cc = strlen(wpath); if (cc == 0) return 0; if (cc > XFCC-20) zappcrash("SearchWild: wpath > XFCC"); pp = (char *) wpath; repl_Nstrs(pp,searchpath,"$","\\$","\"","\\\"",null); // init. search path, escape $ and " 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 \"%s\" -type f -or -type l",searchpath); // find files (ordinary, symlink) 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 } /************************************************************************** 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 5.6 if (! pv->act) printz("meaningless reference %d",ii); // stop g++ optimization bug /// 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() // implicit seed, repeatable sequence { static int64 seed = 23459876; 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. // 5.7 // 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 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/ icon files: filename.png /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/ zlocalesdir translation files /prefix/share/appname/locales/ zuserdir 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 char * get_zprefix() returns install top directory (has /bin and /share under it) char * get_zuserdir() returns /home/user/.appname (or /root/.appname) char * get_zdatadir() returns directory where application data files reside char * get_zdocdir() returns directory for application documentation files char * get_zicondir() returns directory for application icons char * get_zlocalesdir() returns directory for translation files ***/ cchar * get_zprefix() { return zfuncs::zprefix; } // /usr or /home/ cchar * get_zuserdir() { return zfuncs::zuserdir; } // /home/user/.appname cchar * get_zdatadir() { return zfuncs::zdatadir; } // parameters, icons cchar * get_zdocdir() { return zfuncs::zdocdir; } // documentation files cchar * get_zicondir() { return zfuncs::zicondir; } // icon files cchar * get_zlocalesdir() { return zfuncs::zlocalesdir; } // translation files int zinitapp(cchar *appname) { using namespace zfuncs; char work[200]; char logfile[200], oldlog[200]; int cc, secs, err; time_t Tnow; char *chTnow; STATB statdat; FILE *fid; catch_signals(); // catch segfault, do backtrace strcpy(zappname,appname); // save app name 5.6 #ifndef PREFIX // install location #define PREFIX "/usr" #endif strncpy0(work,PREFIX,199); // /usr or /home/ strcpy(zprefix,work); // /prefix strncatv(zdatadir,199,work,"/share/",zappname,"/data",null); // /prefix/share/appname/data strncatv(zicondir,199,work,"/share/",zappname,"/icons",null); // /prefix/share/appname/icons strncatv(zlocalesdir,199,work,"/share/",zappname,"/locales",null); // /prefix/share/appname/locales strncatv(zdocdir,199,work,"/share/doc/",zappname,null); // /prefix/share/doc/appname #ifdef DOCDIR strncpy0(zdocdir,DOCDIR,199); // flexible DOCDIR location #endif snprintf(zuserdir,199,"%s/.%s",getenv("HOME"),zappname); // /home//.appname/ cc = strlen(zuserdir); // stop humongous username 5.3 if (cc > 160) zappcrash("too big: %s",zuserdir); err = stat(zuserdir,&statdat); // does it exist already? if (err) { err = mkdir(zuserdir,0750); // no, create and initialize if (err) zappcrash("cannot create %s",zuserdir); shell_quiet("cp -R %s/* %s",zdatadir,zuserdir); // copy initial data files 6.0 } tid_main = pthread_self(); // thread ID of main() process Tnow = time(0); chTnow = ctime(&Tnow); chTnow[19] = 0; if (! isatty(fileno(stdin))) { // not attached to a terminal 5.7 snprintf(logfile,199,"%s/logfile",zuserdir); // /home//logfile 5.5 snprintf(oldlog,199,"%s/logfile.old",zuserdir); err = stat(logfile,&statdat); if (! err) { secs = Tnow - statdat.st_mtime; // if log file age > 1 hour 5.0 if (secs > 3600) rename(logfile,oldlog); // rename to *.old } 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); // 5.2 return 1; } // 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) { using namespace zfuncs; 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,zuserdir); // /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) // 5.5 { 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 5.2 { using namespace zfuncs; char buff[200]; fflush(0); // 5.2 snprintf(buff,199,"cat %s/logfile",zuserdir); 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) // 5.5 { char filex[40], filespec[200], command[200]; int err; strncpy0(filex,file,36); // look for gzip file first 5.7 strcat(filex,".gz"); err = locale_filespec(type,filex,filespec); if (! err) { snprintf(command,200,"zcat %s",filespec); popup_command(command,600,400,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 = system("which firefox"); // use xdg-open only as last resort if (! err) strcpy(prog,"firefox"); // 5.2 else { err = system("which chromium-browser"); if (! err) strcpy(prog,"chromium-browser"); else { err = system("which xdg-open"); if (! err) strcpy(prog,"xdg-open"); } } } if (! *prog) { zmessLogACK(null,"html file reader not found"); return; } shell_ack("%s %s &",prog,url); return; } /**************************************************************************/ // Creates a desktop icon / launcher and a system menu entry. // The menu name is taken from the input command, without options. // A command like "mycom -optA -optB" would generate a menu name of "mycom". // The categories should be separated by semicolons and conform to LSB categories. // The generic name is free text to describe the application, e.g. "Image Editor". // If the target system is not LSB compliant this function will not work. void zmake_menu_launcher(cchar *command, cchar *categories, cchar *genericname) { using namespace zfuncs; char appname[20], dtdir[200], dtfile[200], work[200]; cchar *xdgcomm = "xdg-user-dir DESKTOP"; cchar *pp; FILE *fid; pp = strField(command,' ',1); if (! pp) pp = "?"; strncpy0(appname,pp,20); fid = popen(xdgcomm,"r"); // get desktop directory for user if (! fid) { // 5.8 zmessageACK(0,"%s \n %s",xdgcomm,strerror(errno)); return; } int nn = fscanf(fid,"%s",dtdir); pclose(fid); if (nn != 1) { zmessageACK(0,"xdg-user-dir DESKTOP failed"); return; } snprintf(dtfile,200,"%s/%s.desktop",dtdir,appname); fid = fopen(dtfile,"w"); if (! fid) { zmessageACK(0,"%s \n %s",dtfile,strerror(errno)); return; } fputs("[Desktop Entry]\n",fid); // [Desktop Entry] snprintf(work,199,"Name=%s\n",appname); // Name=appname fputs(work,fid); snprintf(work,199,"Categories=%s\n",categories); // Categories=Cat1;Cat2; ... fputs(work,fid); snprintf(work,199,"GenericName=%s\n",genericname); // GenericName=generic app name fputs(work,fid); fputs("Type=Application\n",fid); // Type=Application fputs("Terminal=false\n",fid); // Terminal=false snprintf(work,199,"Exec=%s/bin/%s\n",zprefix,command); // Exec=/usr/bin/appname -options fputs(work,fid); snprintf(work,199,"Icon=%s/%s.png\n",zicondir,appname); // /usr/share/appname/icons/appname.png fputs(work,fid); fclose(fid); shell_ack("chmod 0750 %s",dtfile); // make executable shell_ack("xdg-desktop-menu install --novendor %s",dtfile); // add menu entry 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, *fidw; 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 zfuncs; using namespace ZTXnames; int ii, err; char installpo[200], localpo[200]; char *pp, poname[20]; 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; } 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,zuserdir,poname); } else err = shell_ack("cp %s %s/%s",installpo,zuserdir,poname); if (err) return; snprintf(localpo,200,"%s/%s",zuserdir,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); remove(localpo); // remove local .po file 6.1 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 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); if (ii < 0) pp = english; else pp = tstring[ii]; if (strlen(pp) == 0) pp = english; // translation is "" 5.6 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) // 5.6 { 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 "" 5.6 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 5.0 while (gtk_events_pending()) // gdk_flush() removed 5.2 gtk_main_iteration_do(0); // use gtk_main_iteration_do 5.2 return; } /**************************************************************************/ // crash if current execution is not the main() thread void zthreadcrash() { if (pthread_equal(pthread_self(),zfuncs::tid_main)) return; zappcrash("forbidden function called from thread"); return; } // get hardware information via GTK // failure results in null pointers void get_hardware_info(GtkWidget *widget) // 5.9 { using namespace zfuncs; GdkWindow *window; if (mouse) return; // already done zthreadcrash(); window = gtk_widget_get_window(widget); display = gdk_window_get_display(window); if (display) screen = gdk_window_get_screen(window); if (screen) manager = gdk_display_get_device_manager(display); if (manager) mouse = gdk_device_manager_get_client_pointer(manager); if (! mouse) printz("*** GTK cannot get hardware info \n"); settings = gtk_settings_get_for_screen(screen); // 6.2 return; } // move the mouse pointer to given position in given window int move_pointer(GtkWidget *widget, int px, int py) // 5.9 { using namespace zfuncs; int rpx, rpy; GdkWindow *window; zthreadcrash(); get_hardware_info(widget); if (! mouse) return 0; window = gtk_widget_get_window(widget); gdk_window_get_root_coords(window,px,py,&rpx,&rpy); gdk_device_warp(mouse,screen,rpx,rpy); return 1; } /**************************************************************************/ // write message to GTK text view window // line: +N existing lines from top (replace) // -N existing lines from bottom (replace) // 0 next line (add new line at bottom) // scroll logic assumes only one \n per message void wprintx(GtkWidget *mLog, int line, cchar *message, int bold) { GtkTextMark *endMark; GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; GtkTextTag *fontag = 0; int nlines, scroll = 0; cchar *normfont = "monospace 9"; cchar *boldfont = "monospace bold 9"; if (! mLog) { // if no GUI use STDOUT printz("%s",message); return; } zthreadcrash(); // thread usage not allowed textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(mLog)); if (! textBuff) return; // 5.5 endMark = gtk_text_buffer_get_mark(textBuff,"wpxend"); // get my end mark if (! endMark) { gtk_text_buffer_get_end_iter(textBuff,&iter1); // new buffer, set my end mark endMark = gtk_text_buffer_create_mark(textBuff,"wpxend",&iter1,0); } nlines = gtk_text_buffer_get_line_count(textBuff); // lines now in buffer if (line == 0) scroll++; // auto scroll is on if (line < 0) { line = nlines + line + 1; // last lines: -1, -2 ... if (line < 1) line = 1; // above top, use line 1 } if (line > nlines) line = 0; // below bottom, treat as append if (line == 0) gtk_text_buffer_get_end_iter(textBuff,&iter1); // append new line if (line > 0) { gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line-1); // old line start if (line < nlines) gtk_text_buffer_get_iter_at_line(textBuff,&iter2,line); // old line end if (line == nlines) // or buffer end gtk_text_buffer_get_end_iter(textBuff,&iter2); gtk_text_buffer_delete(textBuff,&iter1,&iter2); // delete old line } if (bold) fontag = gtk_text_buffer_create_tag(textBuff,0,"font",boldfont,0); else fontag = gtk_text_buffer_create_tag(textBuff,0,"font",normfont,0); gtk_text_buffer_insert_with_tags(textBuff,&iter1,message,-1,fontag,null); if (scroll) // scroll line into view gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(mLog),endMark,0,0,1,1); zmainloop(); return; } void wprintf(GtkWidget *mLog, int line, cchar *format, ... ) // "printf" version { va_list arglist; char message[1000]; va_start(arglist,format); vsnprintf(message,999,format,arglist); va_end(arglist); wprintx(mLog,line,message); return; } void wprintf(GtkWidget *mLog, cchar *format, ... ) // "printf", scrolling output { va_list arglist; char message[1000]; va_start(arglist,format); vsnprintf(message,999,format,arglist); // stop overflow, remove warning va_end(arglist); wprintx(mLog,0,message); return; } /**************************************************************************/ // scroll a text view window to put a given line on screen // 1st line = 1. for last line use line = 0. void wscroll(GtkWidget *mLog, int line) { GtkTextBuffer *textbuff; GtkTextIter iter; GtkTextMark *mark; if (! mLog) return; zthreadcrash(); // thread usage not allowed textbuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(mLog)); if (! textbuff) return; // 5.6 if (line <= 0) line = gtk_text_buffer_get_line_count(textbuff); line = line - 1; gtk_text_buffer_get_iter_at_line(textbuff,&iter,line); mark = gtk_text_buffer_create_mark(textbuff,0,&iter,0); // bugfix, gtk_text_view_scroll_to_iter() gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(mLog),mark,0,0,1,1); // fails with no error return; } /**************************************************************************/ // clear a text view window and get a new buffer (a kind of defrag) void wclear(GtkWidget *mLog) { GtkTextBuffer *buff; if (! mLog) return; zthreadcrash(); // thread usage not allowed buff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(mLog)); if (! buff) return; // 5.6 gtk_text_buffer_set_text(buff,"",-1); return; } // clear a text view window from designated line to end of buffer void wclear(GtkWidget *mLog, int line) { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; if (! mLog) return; zthreadcrash(); // thread usage not allowed textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(mLog)); if (! textBuff) return; // 5.6 gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line-1); // iter at line start gtk_text_buffer_get_end_iter(textBuff,&iter2); gtk_text_buffer_delete(textBuff,&iter1,&iter2); // delete existing line return; } /**************************************************************************/ // Read from a text widget, one line at a time. // Set ftf = 1 for first call (will be returned = 0). // The next line of text is returned, or null if no more text. // A final \n character is removed if present. // Only one line is remembered, so it must be copied before the next call. char * wscanf(GtkWidget *mLog, int & ftf) { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; static char *precs = 0, *prec1, *pret; static int cc; if (! mLog) return 0; zthreadcrash(); // thread usage not allowed if (ftf) { // get all window text ftf = 0; if (precs) g_free(precs); // free prior memory if there textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(mLog)); // get all text if (! textBuff) return 0; // 5.6 gtk_text_buffer_get_bounds(textBuff,&iter1,&iter2); precs = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); prec1 = precs; // 1st record } if (! precs || (*prec1 == 0)) // no more records { if (precs) g_free(precs); precs = 0; return 0; } cc = 0; while ((prec1[cc] != 0) && (prec1[cc] != '\n')) cc++; // scan for terminator pret = prec1; prec1 = prec1 + cc; // next record if (*prec1 == '\n') prec1++; pret[cc] = 0; // replace \n with 0 return pret; } /**************************************************************************/ // dump text window into file // return: 0: OK +N: error int wfiledump_maxcc = 0; int wfiledump(GtkWidget *mLog, char *filespec) { FILE *fid; char *prec; int ftf, err, cc; if (! mLog) return 0; fid = fopen(filespec,"w"); // open file if (! fid) { zmessageACK(0,ZTX("cannot open file %s"),filespec); return 1; } wfiledump_maxcc = 0; ftf = 1; while (true) { prec = wscanf(mLog,ftf); // get text line if (! prec) break; fprintf(fid,"%s\n",prec); // output with \n cc = strlen(prec); if (cc > wfiledump_maxcc) wfiledump_maxcc = cc; } err = fclose(fid); // close file if (err) { zmessageACK(0,"file close error"); return 2; } else return 0; } /**************************************************************************/ // save text window to file, via file chooser dialog void wfilesave(GtkWidget *mLog, GtkWindow *parent) { int err; char *file; if (! mLog) return; file = zgetfile(ZTX("save screen to file"),parent,"save","screen-save.txt"); if (! file) return; err = wfiledump(mLog,file); if (err) zmessageACK(0,"save screen failed (%d)",err); zfree(file); return; } /**************************************************************************/ // print text window to default printer // use landscape mode if max. print line > A4 width void wprintp(GtkWidget *mLog) { int err; char printfile[30]; static int64 seed = 0; static int pid; if (! seed) seed = pid = getpid(); if (! mLog) return; snprintf(printfile,30,"/tmp/wprintp-%d-%d",pid,lrandz(&seed)); err = wfiledump(mLog,printfile); if (err) return; if (wfiledump_maxcc < 97) err = shell_ack("lp -o %s -o %s -o %s -o %s -o %s -o %s %s", "cpi=14","lpi=8","page-left=50","page-top=50", "page-right=40","page-bottom=40",printfile); else err = shell_ack("lp -o %s -o %s -o %s -o %s -o %s -o %s -o %s %s", "landscape","cpi=14","lpi=8","page-left=50","page-top=50", "page-right=40","page-bottom=40",printfile); return; } /**************************************************************************/ // Set a function to be called when a GTK text view widget is mouse-clicked. // Function returns the clicked line number and position, both zero based. // A wrapped line is still one logical line. // Note: cannot be used on text windows that are edited. // The called function looks like this: // void clickfunc(GtkWidget *widget, int line, int pos) void textwidget_set_clickfunc(GtkWidget *widget, clickfunc_t clickfunc) { int textwidget_mousefunc(GtkWidget *widget, GdkEventButton *event, clickfunc_t clickfunc); zthreadcrash(); gtk_widget_add_events(widget,GDK_ALL_EVENTS_MASK); // connect events 5.2 G_SIGNAL(widget,"event",textwidget_mousefunc,clickfunc); return; } int textwidget_mousefunc(GtkWidget *widget, GdkEventButton *event, clickfunc_t clickfunc) { using namespace zfuncs; static GdkCursor *arrowcursor = 0; GdkWindow *gdkwin; GtkTextIter iter1; int mpx, mpy, tbx, tby, line, pos; #define TEXT GTK_TEXT_WINDOW_TEXT #define VIEW GTK_TEXT_VIEW #define ARROW GDK_TOP_LEFT_ARROW if (! arrowcursor) arrowcursor = gdk_cursor_new_for_display(display,ARROW); gdkwin = gtk_text_view_get_window(VIEW(widget),TEXT); if (gdkwin) gdk_window_set_cursor(gdkwin,arrowcursor); // why must this be repeated? 5.5 if (event->type != GDK_BUTTON_RELEASE) return 0; // 5.7 mpx = int(event->x); // mouse click position mpy = int(event->y); gtk_text_view_window_to_buffer_coords(VIEW(widget),TEXT,mpx,mpy,&tbx,&tby); gtk_text_view_get_iter_at_location(VIEW(widget),&iter1,tbx,tby); line = gtk_text_iter_get_line(&iter1); // clicked line pos = gtk_text_iter_get_line_offset(&iter1); // clicked position clickfunc(widget,line,pos); // call user function return 0; // 5.6 } // get a given line of text from a GTK text view widget // returned text is subject for zfree() char * textwidget_get_line(GtkWidget *widget, int line, int hilite) { GtkTextBuffer *textbuffer; GtkTextIter iter1, iter2; char *text, *ztext; int cc; zthreadcrash(); textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); if (! textbuffer) return 0; // 5.6 gtk_text_buffer_get_iter_at_line(textbuffer,&iter1,line); iter2 = iter1; gtk_text_iter_forward_line(&iter2); if (hilite) // highlight selected line 5.6 gtk_text_buffer_select_range(textbuffer,&iter1,&iter2); text = gtk_text_buffer_get_text(textbuffer,&iter1,&iter2,0); if (! text) return 0; ztext = zstrdup(text); zfree(text); cc = strlen(ztext); while (cc && ztext[cc-1] < ' ') cc--; ztext[cc] = 0; if (cc == 0) { zfree(ztext); return 0; } return ztext; } // get the clicked word within the line // words are defined by line starts and ends, and the given delimiter // returns word and delimiter (&end) char * textwidget_get_word(char *line, int pos, cchar *dlims, char &end) { char *pp1, *pp2, *ztext; int cc; if (! line) return 0; pos = utf8_position(line,pos); // graphic position to byte position if (pos < 0) return 0; // 5.5 pp1 = line + pos; if (! *pp1 || strchr(dlims,*pp1)) return 0; // reject edge position or delimiter while (pp1 > line && ! 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); return ztext; } /************************************************************************** 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) // icon size removed { GtkWidget *wmbar; zthreadcrash(); 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; zthreadcrash(); 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; zthreadcrash(); 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 5.2 return wmsubitem; } /**************************************************************************/ // create toolbar and add to vertical packing box int tbIconSize = 24; // valid during toolbar construction GtkWidget * create_toolbar(GtkWidget *vbox, int iconsize) { using namespace zfuncs; GtkWidget *wtbar; zthreadcrash(); 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) { using namespace zfuncs; GtkToolItem *tbutton; GError *gerror = 0; PIXBUF *pixbuf; GtkWidget *wicon = 0; char iconpath[200]; zthreadcrash(); 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,zicondir,"/",icon,null); 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); // 5.8 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; zthreadcrash(); 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; zthreadcrash(); 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; } /************************************************************************** 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(); zthreadcrash(); gtk_widget_add_events(popmenu,GDK_BUTTON_PRESS_MASK); // 5.5 G_SIGNAL(popmenu,"button-press-event",popmenu_event,0); // no event passed in Ubuntu 13.10 5.7 return popmenu; } // handle mouse button event in a popup menu int popmenu_event(GtkWidget *popmenu, GdkEvent *event) // not called in Ubuntu 13.10 5.7 { 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; zthreadcrash(); 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 // // UGLY KLUDGE: If mouse is near screen bottom edge, popup menu will // be partly below the screen and invisible. The following work-around // is implemented: If within 600 of screen bottom, show at bottom - 600 // to insure entire popup is visible. Get the true height of the realized // widget. If within height of screen bottom, show at bottom - height. // Otherwise show at mouse position. void popup_menu(GtkWidget *widget, GtkWidget *popmenu) { void popup_moveloc(GtkMenu *, int *, int *, int *, void *); // 6.1 int int600 = 600; GtkRequisition req; zthreadcrash(); gtk_menu_popup(GTK_MENU(popmenu),0,0,popup_moveloc,&int600,1,0); // show popup at screen bottom - 600 gtk_widget_show_all(popmenu); gtk_widget_get_preferred_size((GtkWidget *) popmenu,null,&req); // get size of realized popup menu gtk_menu_popup(GTK_MENU(popmenu),0,0,popup_moveloc,&req.height,1,0); // show at bottom - height gtk_widget_show_all(popmenu); return; } // Move popup menu 40 pixels to the right of the mouse. // If within 'vphh' of the screen bottom, move it up to bottom - 'vphh'. void popup_moveloc(GtkMenu *menu, int *mx, int *my, int *, void *vphh) // 6.1 { using namespace zfuncs; int phh, scrhh; get_hardware_info(GTK_WIDGET(menu)); if (! mouse) return; scrhh = gdk_screen_get_height(screen); gdk_device_get_position(mouse,&screen,mx,my); *mx += 40; phh = *((int *) vphh); if (scrhh - *my < phh) *my = scrhh - phh - 5; // if under bottom - height, move up return; } /************************************************************************** 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 #define menuFont "sans 9" // menu font #define iconSize 24 // menu icon size (default) typedef void callbackfunc(cchar *menu); // user callback function callbackfunc *gmenuzcallback; char *menuconfigfile = 0; // 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 on null 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 }; 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 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 edit_menu(); // dialog to create/edit menu entry int edit_menu_event(zdialog *zd, cchar *event); // dialog 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; zthreadcrash(); if (Fpopbusy) return; // don't allow multiple popups pWin = parent; // get parent window if (menuconfigfile) zfree(menuconfigfile); menuconfigfile = zstrdup(ufile); // get menu configuration file gmenuzcallback = ufunc; // get user callback function NME = 0; fid = fopen(menuconfigfile,"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,"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 > 64) size = 64; menus[me].size = size; } if (strmatchN(pp,"kill",4)) // kill window flag menus[me].kill = 1; } fclose(fid); } Fchanged = 0; // no changes yet Fpopquit = 0; // not being closed Fpopbusy = 1; mWin = gtk_window_new(GTK_WINDOW_TOPLEVEL); // create popup window for menu entries if (! pWin) pposx = pposy = 0; // no parent window 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); // 5.8 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,"key-release-event",KB_event,0); // connect KB key release 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); gtk_widget_show_all(mWin); // show all widgets 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; 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 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 = iconSize; // use default if not specified } else size = 0; yadd = 0; if (pixbuf) yadd = size + 2; // extra space under icon if (text) { text2 = (char *) zmalloc(strlen(text)+2); // replace "\n" with newline repl_1str(text,text2,"\\n","\n"); draw_text(cr,text2,xpos,ypos+yadd,ww,hh); // returns size of text written 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 && ww == winww && hh == winhh) return; // no change winposx = xx; winposy = yy; winww = ww; winhh = hh; Fchanged = 1; // mark window changed return; } // [x] kill window // Save current menu status for next session. void gmenuznames::quit() { using namespace gmenuznames; zthreadcrash(); Fpopquit = 1; Fpopbusy = 0; if (Fchanged) update_configfile(); gtk_widget_destroy(mWin); gmenuzcallback("quit"); // inform host program 5.4 return; } // menu changed, save all menu data to menu config. file void gmenuznames::update_configfile() { using namespace gmenuznames; char *pp, *pxbfile; int pposx, pposy; FILE *fid; GError *gerror; zthreadcrash(); 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(menuconfigfile,"w"); // open for write if (! fid) { zmessageACK(mWin," %s \n %s",menuconfigfile,strerror(errno)); // diagnose permissions error 5.7 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: skip 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].func) // menu function (text) fprintf(fid,"func %s \n",menus[me].func); if (menus[me].pixbuf) { // pixbuf image for menu icon pxbfile = zstrdup(menuconfigfile,20); pp = pxbfile + strlen(pxbfile); // create a local PNG file for pixbuf snprintf(pp,20,"-pixbuf-%03d.png",me); gerror = 0; gdk_pixbuf_save(menus[me].pixbuf,pxbfile,"png",&gerror,null); // write pixbuf to file if (gerror) printz("*** %s \n %s \n",menus[me].menu,gerror->message); else fprintf(fid,"icon %s \n",pxbfile); // pixbuf file name in menu file zfree(pxbfile); } 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; 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 if (elapsed < 500 && ! Fdrag) Fclick = 1; // mouse clicked if (me >= 0 && Fclick && Lmouse) { // menu entry was left-clicked if (menus[me].func) gmenuzcallback(menus[me].func); // caller user function(func) if (menus[me].kill) quit(); // close menu after app launch } else if (Fclick && Rmouse) // menu entry or empty space right-clicked edit_menu(); // edit menu else if (me >= 0 && Fdrag) { // menu entry drag ended xpos = menus[me].xpos; // align to 16-pixel raster ypos = menus[me].ypos; xpos = 16 * ((xpos + 8) / 16) + 4; // min. 4 pixels from edge ypos = 16 * ((ypos + 8) / 16) + 4; menus[me].xpos = xpos; menus[me].ypos = ypos; gtk_widget_queue_draw(layout); // repaint window Fdrag = 0; Fchanged = 1; // mark menu revised } Lmouse = Rmouse = 0; // mouse click action completed } if (event->type == GDK_MOTION_NOTIFY) // mouse movement { if (me >= 0 && Lmouse) { // menu drag underway dx = mpx - mpx0; dy = mpy - mpy0; if (Fdrag || (abs(dx) + abs(dy) > 15)) { // ignore small drags 5.5 Fdrag++; mpx0 = mpx; // set new drag origin mpy0 = mpy; menus[me].xpos += dx; // add motion to image position menus[me].ypos += dy; gtk_widget_queue_draw(layout); // repaint window } } } return; } // KB release event function - send KB F1 key to main app void gmenuznames::KB_event(GtkWidget *, GdkEventKey *kbevent, void *) { int KBkey = kbevent->keyval; if (KBkey == GDK_KEY_F1) KBstate(kbevent,0); // 5.6 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; zthreadcrash(); 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; } // dialog to create a new menu entry from user inputs void gmenuznames::edit_menu() { using namespace gmenuznames; zthreadcrash(); 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 [________________________] 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,"apply","delete","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","menu text"); zdialog_add_widget(zdedit,"label","lab12","vb1","menu func"); zdialog_add_widget(zdedit,"label","lab13","vb1","menu icon"); zdialog_add_widget(zdedit,"label","lab14","vb1","icon size"); zdialog_add_widget(zdedit,"entry","text","vb2",0,"size=40|space=2"); zdialog_add_widget(zdedit,"entry","func","vb2",0,"size=40|space=2"); zdialog_add_widget(zdedit,"hbox","hb2","vb2",0,"expand|space=2"); zdialog_add_widget(zdedit,"entry","icon","hb2",0,"expand"); zdialog_add_widget(zdedit,"button","browse","hb2","browse","space=5"); zdialog_add_widget(zdedit,"hbox","hb3","vb2",0,"space=2"); zdialog_add_widget(zdedit,"spin","size","hb3","24|64|1|24"); zdialog_add_widget(zdedit,"check","kill","hb3","close window","space=30"); zdialog_run(zdedit,edit_menu_event); } 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",""); 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; 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) // dialog complete { 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 0; } // [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,"func",text,maxText); // menu function name strTrim2(text); 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; } else { menus[me].icon = 0; menus[me].pixbuf = 0; menus[me].size = 0; } 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 0; } /************************************************************************** Vertical Menu / Toolbar Build a custom vertical menu and/or toolbar in a vertical packing box vbm = Vmenu_new(GtkWidget *vbox) 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) Create a vertical menu / toolbar in a vertical packing box. 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 menufont1 "sans 10" // menu font #define menufont2 "sans bold 10" // menu font (selected) #define fontheight 18 // menu text height in layout #define margin 5 // margins for menu text #define BGCOLOR 59000,59000,59000 // = cairo 0.9,0.9,0.9 PangoFontDescription *pfont1, *pfont2; PangoAttrList *pattrlist; PangoAttribute *pbackground; 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) // revised 5.7 { using namespace Vmenunames; int cc; zthreadcrash(); cc = sizeof(Vmenu); Vmenu *vbm = (Vmenu *) zmalloc(cc); memset(vbm,0,cc); vbm->vbox = 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; // layout size 5.8 pattrlist = pango_attr_list_new(); pbackground = pango_attr_background_new(BGCOLOR); pango_attr_list_change(pattrlist,pbackground); pfont1 = pango_font_description_from_string(menufont1); pfont2 = pango_font_description_from_string(menufont2); 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, cc1, cc2, xpos, ww, hh; char iconpath[200], *mdesc, *name__; cchar *blanks = " "; // 20 blanks PIXBUF *pixbuf; GError *gerror = 0; PangoLayout *playout; PangoFontDescription *pfont; zthreadcrash(); 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; // 5.8 vbm->menu[me].iconhh = iconhh; } if (desc) { // pad description with blanks for looks cc1 = strlen(desc); // 5.6 mdesc = (char *) zmalloc(cc1+3); mdesc[0] = ' '; strcpy(mdesc+1,desc); strcpy(mdesc+cc1+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::zicondir,"/",icon,null); pixbuf = gdk_pixbuf_new_from_file_at_scale(iconpath,iconww,iconhh,1,&gerror); if (pixbuf) vbm->menu[me].pixbuf = pixbuf; } 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 + 2; } 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; cc1 = strlen(name); // prepare menu name with trailing blanks cc2 = 0.3 * cc1 + 3; // so normal name longer than bold name if (cc2 > 20) cc2 = 20; // otherwise bold not 100% overwritten name__ = zstrdup(name,cc2); strncpy0(name__+cc1,blanks,cc2); 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; } // paint window when created, exposed, resized void Vmenunames::wpaint(GtkWidget *widget, cairo_t *cr, Vmenu *vbm) { using namespace Vmenunames; cairo_set_source_rgb(cr,0.9,0.9,0.9); // white background 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,0.9,0.9,0.9); 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,0.5,0.5,0.5); // 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,0,0,0); 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; cchar *desc; int me, mpx, mpy, button, ww, ylo, yhi; static int me0 = -1; cairo_t *cr; gdkwin = gtk_layout_get_bin_window(GTK_LAYOUT(widget)); cr = gdk_cairo_create(gdkwin); mpx = int(event->x); // mouse position mpy = int(event->y); button = event->button; if (event->type == GDK_MOTION_NOTIFY) // mouse inside layout { 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); me0 = -1; } if (me == me0) goto MEreturn; // same as before if (me == vbm->mcount) goto MEreturn; // 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(desc,30,20,0,5); me0 = me; // remember last match goto MEreturn; } 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); me0 = -1; } if (event->type == GDK_BUTTON_RELEASE) // menu entry clicked { 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) goto MEreturn; // no menu match zfuncs::vmenuclickbutton = button; // 1/2/3 = left/mid/right button 5.9 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; // 5.8 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) } MEreturn: cairo_destroy(cr); 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); // 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 { using namespace zfuncs; zdialog *zd; GtkWidget *dialog, *hbox, *vbox, *butt, *hsep; cchar *bulab[zdmaxbutts]; int cc, ii, nbu; va_list arglist; zthreadcrash(); // thread usage not allowed 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 = " "; // 5.0 dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); // use window, not dialog 5.5 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 5.9 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 5.9 } } zd->compbutton[nbu] = 0; // mark EOL 5.8 //// 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->parent = parent; // parent window or null zd->sentinel1 = zdsentinel | (lrandz() & 0x0000FFFF); // validity sentinels 5.8 zd->sentinel2 = zd->sentinel1; // fixed part + random part 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)); 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 / hscale / vscale: use format "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) { using namespace zfuncs; GtkWidget *widget = 0, *pwidget = 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; zthreadcrash(); 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); // all strings in nonvolatile mem 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].stopKB = 0; zd->widget[iiw+1].type = 0; // set new EOF marker if (strmatchV(type,"dialog","hbox","vbox","hsep","vsep","frame","scrwin", "label","link","entry","edit","text","button","togbutt", "imagebutt","colorbutt","check","combo","comboE","radio", "spin","hscale","vscale","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 5.4 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; // 5.3 data not further used } 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); data = 0; // 5.3 data not further used } if (strmatch(type,"label")) { // label (static text) widget = gtk_label_new(data); data = 0; // 5.3 data not further used } if (strmatch(type,"link")) { // label is clickable if (strmatch(name,"nolabel")) widget = gtk_link_button_new(data); // link is also label else // 5.6 widget = gtk_link_button_new_with_label(data,name); // label >> link G_SIGNAL(widget,"clicked",zdialog_widget_event,zd); data = 0; // 5.3 data not further used } if (strmatch(type,"entry")) { // 1-line text entry 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); /// fix huge default entry widgets but not buildable with gtk 3.10 (ubuntu 14.04) ////// /// if (! expand) gtk_entry_set_max_width_chars(GTK_ENTRY(widget),size); ////// G_SIGNAL(widget,"changed",zdialog_widget_event,zd); } if (strmatch(type,"edit")) { // multiline text input widget = gtk_text_view_new(); 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),1); if (wrap) gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget),GTK_WRAP_WORD); 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(); 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; // 5.3 data not further used } if (strmatch(type,"togbutt")) { // toggle button widget = gtk_toggle_button_new_with_label(data); G_SIGNAL(widget,"toggled",zdialog_widget_event,zd); data = "0"; // 5.3 default data } if (strmatch(type,"imagebutt")) { // button with image 6.0 snprintf(iconpath,200,"%s/%s",get_zicondir(),data); 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 5.8 pp = strField(data,'|',2); gdkrgba.green = f256 * atoi(pp); pp = strField(data,'|',3); gdkrgba.blue = f256 * atoi(pp); gdkrgba.alpha = 1.0; // 5.8 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"; // 5.3 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"; // 5.3 default data } if (strmatchV(type,"spin","hscale","vscale",null)) { // spin button or sliding scale if (! data) zappcrash("zdialog_add_widget(): data missing"); // 5.8 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,"image")) { // image widget 5.7 snprintf(iconpath,200,"%s/%s",get_zicondir(),data); 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); } // all widget types come here zd->widget[iiw].widget = widget; // set widget in zdialog if (strmatchV(type,"entry","edit","comboE",null) > 0) // for data entry widgets, stop zd->widget[iiw].stopKB = 1; // propagation of KB events 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 5.5 gtk_box_pack_start(GTK_BOX(vbox),widget,expand,expand,space); } if (data) zd->widget[iiw].data = zstrdup(data); // use heap 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 { using namespace zfuncs; int ok, ii; if (! zd) return 0; for (ii = 0; ii < zdialog_count; ii++) // find in valid zdialog list 5.9 if (zd == zdialog_list[ii]) break; if (ii == zdialog_count) { if (errmess) printz("*** zdialog invalid %p \n",__builtin_return_address(0)); 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; } // add a popup tool tip to a zdialog widget int zdialog_add_ttip(zdialog *zd, cchar *wname, cchar *ttip) // 5.9 { GtkWidget *widget; int ii; zthreadcrash(); 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; zthreadcrash(); 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) { zthreadcrash(); 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; cchar *type, *pp; char *wdata; double dval; double f256 = 1.0 / 256.0; float lval, hval, nval, F, F2; zthreadcrash(); // thread usage not allowed 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) { wdata = zstrdup(data); // set new data for widget zd->widget[iiw].data = wdata; if (utf8_check(wdata)) printz("*** zdialog: bad UTF8 encoding %s \n",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,"button")) // change button label 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); // generates GTK error messages **** } if (strmatch(type,"text")) { // text output 5.5 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,"colorbutt")) { // color button data is nnn|nnn|nnn pp = strField(data,'|',1); if (pp) gdkrgba.red = f256 * atoi(pp); // RGB range is 0-1 5.8 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); // 5.8 } 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) { F2 = (nval - dval) / (nval - lval); 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 } 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, hscale, vscale) int zdialog_set_limits(zdialog *zd, cchar *name, double min, double max) { GtkWidget *widget; cchar *type; int iiw; zthreadcrash(); 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); 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 int zdialog_rescale(zdialog *zd, cchar *name, float lval, float nval, float hval) { int iiw; zthreadcrash(); 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; } 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. // // KBstate: extern void KBstate(GdkEventKey *event, int state) // This function must be supplied by the caller of zdialog. // It is called when Ctrl|Shift|Alt|F1 is pressed or released. int zdialog_run(zdialog *zd, zdialog_event evfunc, cchar *posn) { int zdialog_KBpress(GtkWidget *, GdkEventKey *event, zdialog *zd); int zdialog_KBrelease(GtkWidget *, GdkEventKey *event, zdialog *zd); int zdialog_focus_event_signal(GtkWidget *, GdkEvent *event, zdialog *zd); int ii; GtkWidget *widget, *dialog, *entry; zthreadcrash(); if (! zdialog_valid(zd)) zappcrash("zdialog invalid"); dialog = zd->widget[0].widget; if (posn) zdialog_set_position(zd,posn); // put dialog at remembered position for (ii = 1; zd->widget[ii].type; ii++) // *** stop auto-selection { // (GTK "feature") if (strmatch(zd->widget[ii].type,"entry")) { widget = zd->widget[ii].widget; if (gtk_editable_get_editable(GTK_EDITABLE(widget))) gtk_editable_set_position(GTK_EDITABLE(widget),-1); break; } if (strmatch(zd->widget[ii].type,"comboE")) { // also combo edit box widget = zd->widget[ii].widget; entry = gtk_bin_get_child(GTK_BIN(widget)); gtk_editable_set_position(GTK_EDITABLE(entry),-1); break; } } 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) // if no parent, force to top gtk_window_set_keep_above(GTK_WINDOW(dialog),1); G_SIGNAL(dialog,"focus-in-event",zdialog_focus_event_signal,zd); // connect focus event function 5.0 G_SIGNAL(dialog,"key-press-event",zdialog_KBpress,zd); // connect key press event function G_SIGNAL(dialog,"key-release-event",zdialog_KBrelease,zd); // connect key release event function G_SIGNAL(dialog,"delete-event",zdialog_delete_event,zd); // connect delete event function 5.5 zd->zstat = 0; // dialog not complete 5.7 zd->disabled = 0; // enable widget events zfuncs::zdialog_busy++; // count open zdialogs return 0; // return now, dialog is non-modal } // 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; static GtkWidget *lastwidget = 0; int ii, nn; cchar *wname, *type, *wdata; static char event[40]; // preserve from dialog destroy 5.9 char sdata[20]; double dval; float lval, nval, hval, F; static int cbadded = 0; if (! zdialog_valid(zd)) return 0; if (zd->disabled) return 1; // re-entry from zdialog_put_data() zd->disabled = 1; // or user event function 5.8 for (ii = 0; ii < zdmaxbutts; ii++) { // check completion buttons if (zd->compwidget[ii] == null) break; // EOL 5.8 if (zd->compwidget[ii] != widget) continue; zd->zstat = ii+1; // zdialog status = button no. strncpy0(event,"zstat",40); goto call_evfunc; // call zdialog event function 5.7 } 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 (strmatch(zd->widget[ii].type,"edit")) { // of text view widget 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; type = zd->widget[ii].type; wdata = 0; if (strmatch(type,"button")) wdata = "clicked"; if (strmatch(type,"entry")) wdata = gtk_entry_get_text(GTK_ENTRY(widget)); if (strmatch(type,"edit")) { gtk_text_buffer_get_bounds(textBuff,&iter1,&iter2); wdata = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); } if (strmatchV(type,"radio","check","togbutt",null)) { nn = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); if (nn == 0) wdata = "0"; else wdata = "1"; } if (strmatch(type,"combo")) wdata = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(widget)); if (strmatch(type,"comboE")) { if (widget == 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(type,"spin")) { dval = gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget)); snprintf(sdata,20,"%g",dval); wdata = sdata; } if (strmatch(type,"colorbutt")) // color button 5.8 { 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(type,"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 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 lastwidget = widget; // remember last widget updated strncpy0(event,wname,40); // event = widget name call_evfunc: // call zdialog event function 5.7 if (zd->eventCB) { evfunc = (zdialog_event *) zd->eventCB; // do callback function evfunc(zd,event); } zd->stopKB = zd->widget[ii].stopKB; // stop KB propagation if req. 5.1 zd->disabled = 0; // re-enable widgets return 1; } // zdialog response handler for "focus-in-event" signal // 5.0 // private function zdialog *zdialog_focus_zd; // 5.8 current zdialog int zdialog_focus_event_signal(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 KBstate(GdkEventKey *event, int state) // private function int KB_Ctrl = 0; // track state of KB Ctrl int KB_Shift = 0; // and Shift and Alt keys int KB_Alt = 0; int zdialog_KBpress(GtkWidget *, GdkEventKey *kbevent, zdialog *zd) { KB_Ctrl = KB_Shift = KB_Alt = 0; if (kbevent->state & GDK_CONTROL_MASK) KB_Ctrl = 1; // 5.6 if (kbevent->state & GDK_SHIFT_MASK) KB_Shift = 1; if (kbevent->state & GDK_MOD1_MASK) KB_Alt = 1; if (KB_Ctrl || KB_Alt) { // if Ctrl or Alt pressed, 5.6 KBstate(kbevent,1); // send next key to main app return 1; } return 0; // send to dialog widget } int zdialog_KBrelease(GtkWidget *widget, GdkEventKey *kbevent, zdialog *zd) { void zdialog_copyfunc(GtkWidget *, GtkClipboard *); void zdialog_pastefunc(GtkClipboard *, cchar *, void *); GtkClipboard *clipboard = 0; int KBkey = kbevent->keyval; KB_Ctrl = KB_Shift = KB_Alt = 0; if (kbevent->state & GDK_CONTROL_MASK) KB_Ctrl = 1; // 5.6 if (kbevent->state & GDK_SHIFT_MASK) KB_Shift = 1; if (kbevent->state & GDK_MOD1_MASK) KB_Alt = 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 5.8 { clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); gtk_clipboard_request_text(clipboard,zdialog_pastefunc,widget); return 1; } if (KB_Ctrl || KB_Alt) { // if Ctrl or Alt pressed, KBstate(kbevent,0); // send next key to main app 5.6 return 1; } if (KBkey == GDK_KEY_F1) { // send F1 (help) to main app 5.6 KBstate(kbevent,0); return 1; } if (KBkey == GDK_KEY_F11) { // F11 (toggle full screen) to main app KBstate(kbevent,0); // 5.6 return 1; } if (KBkey == GDK_KEY_Escape) { // ESC key 6.1 zdialog_send_event(zd,"escape"); return 1; } if (KBkey == GDK_KEY_Return) { // Return (Enter) key 5.8 zdialog_send_event(zd,"enter"); return 1; } if (zd->stopKB) return 1; // ignore KB events 5.1 return 0; // send KB input to dialog widget } // 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 (strmatch(zd->widget[ii].type,"edit")) { // of text view widget 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) // 5.8 // 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 (strmatch(zd->widget[ii].type,"edit")) { // of text view widget 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 5.9 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; zthreadcrash(); widget = zdialog_widget(zd,"dialog"); if (! widget) return 0; if (show) { // show window if (widget == pwidget) { // restore prior position 5.4 gtk_window_move(GTK_WINDOW(widget),posx,posy); pwidget = 0; } gtk_widget_show_all(widget); // GTK bug?? window can no longer //// } // receive focus events else { // hide window pwidget = widget; gtk_window_get_position(GTK_WINDOW(widget),&posx,&posy); // save position 5.4 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) { using namespace zfuncs; zthreadcrash(); if (! zdialog_valid(zd)) return 0; if (zd->zstat < 0) { // destroyed by [x] button or GTK if (zd->widget[0].widget) zdialog_busy--; // bugfix 5.8 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 if (zd->parent) gtk_window_present(GTK_WINDOW(zd->parent)); // focus on parent window 5.7 return 1; } // free zdialog memory (will destroy first, if not already) // zd is set to null int zdialog_free(zdialog *&zd) // reference { using namespace zfuncs; int ii; zthreadcrash(); if (! zdialog_valid(zd)) return 0; // validate zd pointer zdialog_save_inputs(zd); // save user inputs for next use 5.3 zdialog_destroy(zd); // destroy GTK dialog if there zd->sentinel1 = zd->sentinel2 = 0; // mark sentinels invalid 5.8 zfree(zd->widget[0].data); // bugfix memory leak 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); zfree((char *) zd->widget[ii].pname); if (zd->widget[ii].data) zfree(zd->widget[ii].data); // free data } for (ii = 0; ii < zdialog_count; ii++) // remove from valid zdialog list 5.9 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; zthreadcrash(); 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; zthreadcrash(); 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 > "" 5.2 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,"%g",ddata); // outputs decimal point or comma zdialog_put_data(zd,name,string); // (per locale) return 1; } 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) // set label text with font 6.2 { GtkWidget *widget; cchar *format = "%s"; char txt2[1000]; 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; } /************************************************************************** Widget type Combo Box 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; zthreadcrash(); 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; zthreadcrash(); 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); // append 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; zthreadcrash(); 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; zthreadcrash(); 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; zthreadcrash(); 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)); return 1; } /**************************************************************************/ // 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 zfuncs; 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",zuserdir); // /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(). // 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; zthreadcrash(); 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 = gdk_screen_width(); phh = gdk_screen_height(); } 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(). 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; zthreadcrash(); 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 5.7 parent = zd->parent; // parent window if (! parent) { // no parent window ppx = ppy = 0; // use desktop pww = gdk_screen_width(); phh = gdk_screen_height(); } 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 { 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[200]; // space for 200 dialogs #define Nwmax zdmaxwidgets // max. widgets in a dialog int Nzd = 0; // no. zdialogs in use int Nzdmax = 200; // max. zdialogs int ccmax1 = 99; // max. widget name length int ccmax2 = 199; // max. widget data length } // 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) // 5.3 { using namespace zfuncs; using namespace zdinputs_names; char zdinputsfile[200], buff[200]; char zdtitle[100], wname[Nwmax][100], wdata[Nwmax][200]; char *pp, *pp1, *pp2, wdata2[250]; // ccmax2 + 50 FILE *fid; int Nw, ii, jj, cc, cc1, cc2; snprintf(zdinputsfile,199,"%s/zdialog_inputs",zuserdir); // /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,200,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,200,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,200,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(). int zdialog_save_inputs(zdialog *zd) // 5.3 { using namespace zdinputs_names; char zdtitle[100], wname[100], wdata[200], *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 " // skip non-input widgets "scrwin label link button",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 label link button",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) // 5.3 { 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; } /**************************************************************************/ // Output text to a popup window. // Window is left on screen until user destroys it with [x] button or // caller closes it with "close" action. // action: open: create window with title = text and size = ww, hh // write: write text to next line in window // writebold: write bold text to next line in window // top: scroll back to line 1 after writing last line // close: close window now // close N: close window after N seconds // The parent argument is optional and will cause the popup window to center // on the parent window. The GtkTextView window is returned for possible // use with textwidget_set_clickfunc() etc. GtkWidget * write_popup_text(cchar *action, cchar *text, int ww, int hh, GtkWidget *parent) { int popup_text_timeout(GtkWidget **mWin); static GtkWidget *mWin = 0, *mVbox, *mScroll; static GtkWidget *mLog = 0; static GtkAdjustment *vscroll; double upper; int secs; char buff[200]; zthreadcrash(); // thread usage not allowed if (strmatch(action,"open")) { if (mWin) gtk_widget_destroy(mWin); // only one at a time if (! ww) ww = 400; if (! hh) hh = 300; mWin = gtk_window_new(GTK_WINDOW_TOPLEVEL); // create main window gtk_window_set_title(GTK_WINDOW(mWin),text); gtk_window_set_default_size(GTK_WINDOW(mWin),ww,hh); if (parent) { // parent added gtk_window_set_transient_for(GTK_WINDOW(mWin),GTK_WINDOW(parent)); gtk_window_set_position(GTK_WINDOW(mWin),GTK_WIN_POS_CENTER_ON_PARENT); } else gtk_window_set_position(GTK_WINDOW(mWin),GTK_WIN_POS_MOUSE); mVbox = gtk_box_new(VERTICAL,0); // vertical packing box gtk_container_add(GTK_CONTAINER(mWin),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 gtk_container_add(GTK_CONTAINER(mScroll),mLog); // add to scrolled window G_SIGNAL(mWin,"destroy",write_popup_text,"destroypop"); // connect window destroy event gtk_widget_show_all(mWin); // show window if (! parent) gtk_window_set_keep_above(GTK_WINDOW(mWin),1); vscroll = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(mScroll)); zfuncs::open_popup_windows++; } if (! mWin) return 0; if (strmatch(action,"write")) { // add text to window snprintf(buff,200,"%s\n",text); wprintx(mLog,0,buff); upper = gtk_adjustment_get_upper(vscroll); // scroll down 6.1 gtk_adjustment_set_value(vscroll,upper); // (GTK did not need this earlier) } if (strmatch(action,"writebold")) { // add bold text to window snprintf(buff,200,"%s\n",text); wprintx(mLog,0,buff,1); upper = gtk_adjustment_get_upper(vscroll); // scroll down 6.1 gtk_adjustment_set_value(vscroll,upper); // (GTK did not need this earlier) } if (strmatch(action,"top")) // scroll to top line wscroll(mLog,1); if (strmatchN(action,"close",5)) { // close window secs = atoi(action+5); if (secs < 1) { gtk_widget_destroy(mWin); // immediate destroy mWin = 0; } else g_timeout_add_seconds(secs,(GSourceFunc) popup_text_timeout,&mWin); } if (text && strmatch(text,"destroypop")) { // "destroy" signal from [x] zfuncs::open_popup_windows--; mWin = 0; } zmainloop(); return mLog; } // private function for timeout int popup_text_timeout(GtkWidget **mWin) { if (*mWin) gtk_widget_destroy(*mWin); return 0; } /**************************************************************************/ // 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; write_popup_text("open",command,ww,hh,parent); // bugfix while ((buff = command_output(contx,command))) { write_popup_text("write",buff); zfree(buff); } if (top) write_popup_text("top",0); // 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]; cchar *posn = "mouse"; zdialog *zd; va_start(arglist,format); vsnprintf(message,999,format,arglist); va_end(arglist); if (! pthread_equal(pthread_self(),zfuncs::tid_main)) { // from a thread, no GTK allowed zpopup_message(0,message); // alternative popup return; } zd = zdialog_new(0,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 zdialog_run(zd,0,posn); zdialog_wait(zd); zdialog_free(zd); return; } /**************************************************************************/ // log error message to STDOUT as well as message box and user OK void zmessLogACK(GtkWidget *parent, cchar *format, ...) { va_list arglist; char message[1000]; va_start(arglist,format); vsnprintf(message,999,format,arglist); va_end(arglist); printz("%s \n",message); zmessageACK(parent,message); 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]; zdialog *zd; int zstat; zthreadcrash(); // thread usage not allowed va_start(arglist,format); vsnprintf(message,400,format,arglist); va_end(arglist); zd = zdialog_new("message",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,"mouse"); // 5.5 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() zdialog * zmessage_post(GtkWidget *parent, int seconds, cchar *format, ... ) { int zmessage_post_timeout(zdialog *zd); int zmessage_post_event(zdialog *zd, cchar *event); va_list arglist; char message[400]; static zdialog *zd; zthreadcrash(); // thread usage not allowed va_start(arglist,format); vsnprintf(message,400,format,arglist); va_end(arglist); zd = zdialog_new(0,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,zmessage_post_event); if (seconds) g_timeout_add_seconds(seconds,(GSourceFunc) zmessage_post_timeout,zd); zmainloop(); // 5.2 return zd; } int zmessage_post_timeout(zdialog *zd) { if (! zdialog_valid(zd)) return 0; if (zd->zstat == 0) zdialog_free(zd); return 0; } int zmessage_post_event(zdialog *zd, cchar *event) { if (! zdialog_valid(zd)) return 0; if (zd->zstat) 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; zthreadcrash(); // thread usage not allowed 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"); // 5.5 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, Nbutts; cchar *butts[6]; zthreadcrash(); // thread usage not allowed va_start(arglist,message); for (ii = 0; ii < 5; ii++) { butts[ii] = va_arg(arglist,cchar *); if (! butts[ii]) break; } Nbutts = ii; if (! Nbutts) zappcrash("zdialog_choose(), no buttons"); repeat: zd = zdialog_new(0,parent,butts[0],butts[1],butts[2],butts[3],butts[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,"mouse"); // 5.5 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; zthreadcrash(); 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_kill(char *current) { using namespace poptext; zthreadcrash(); 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 // 5.8 int poptext_killnow() { using namespace poptext; zthreadcrash(); 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. // 5.6 // 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(cchar *text, int dx, int dy, float secs1, float secs2) // 5.9 { using namespace poptext; GtkWidget *label; int cc, mx, my; int millisec1, millisec2; zthreadcrash(); // thread usage not allowed 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_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_kill,pcurrent); } return; } // Show a popup text message at the given window position. // 5.6 // 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; zthreadcrash(); // thread usage not allowed 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_kill,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. int popup_image(cchar *file, GtkWindow *parent, int Fnewin, int size) // 6.0 { int popup_image_paint(GtkWidget *, cairo_t *, cchar *); int popup_image_scroll(GtkWidget *, GdkEventButton *event, cchar *); int popup_image_KBevent(GtkWidget *, GdkEventKey *event, cchar *); static GtkWidget *window[10], *drawarea[10]; // up to 10 popup windows open static char *filex[10]; static int Nw = 0; zthreadcrash(); if (Fnewin) if (++Nw == 10) Nw = 0; // new window wanted, 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]); G_SIGNAL(drawarea[Nw],"draw",popup_image_paint,filex[Nw]); // connect paint function & image file gtk_widget_add_events(drawarea[Nw],GDK_SCROLL_MASK); G_SIGNAL(drawarea[Nw],"scroll-event",popup_image_scroll,filex[Nw]); // connect mouse wheel scroll event gtk_widget_add_events(drawarea[Nw],GDK_SCROLL_MASK); G_SIGNAL(window[Nw],"key-release-event",popup_image_KBevent,filex[Nw]); gtk_widget_show_all(window[Nw]); return 0; } int popup_image_paint(GtkWidget *drawarea, cairo_t *cr, cchar *file) { GtkWidget *window; PIXBUF *pixb1, *pixb2; GError *gerror; int ww1, hh1, ww2, hh2; int sww, shh; double area; cchar *pp; if (! file) return 1; window = gtk_widget_get_parent(drawarea); // parent window if (! window) 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 = gdk_screen_get_width(zfuncs::screen); // screen dimensions shh = gdk_screen_get_height(zfuncs::screen); 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); // paint image cairo_paint(cr); g_object_unref(pixb1); g_object_unref(pixb2); return 1; } int popup_image_scroll(GtkWidget *drawarea, GdkEventButton *event, cchar *file) { GtkWidget *window; int scroll, ww, hh; int sww, shh; double ff = 1.0; if (event->type == GDK_SCROLL) { // mouse whell event scroll = ((GdkEventScroll *) event)->direction; if (scroll == GDK_SCROLL_UP) ff = 1.33333; if (scroll == GDK_SCROLL_DOWN) ff = 0.75; } window = gtk_widget_get_parent(drawarea); // parent window if (! window) return 1; sww = gdk_screen_get_width(zfuncs::screen); // screen dimensions shh = gdk_screen_get_height(zfuncs::screen); gtk_window_get_size(GTK_WINDOW(window),&ww,&hh); // current window dimensions ww *= ff; hh *= ff; if (ww > sww || hh > shh) { // if request > screen size gtk_window_fullscreen(GTK_WINDOW(window)); // use fullscreen mode return 1; } else gtk_window_unfullscreen(GTK_WINDOW(window)); // else no fullscreen mode 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; } int popup_image_KBevent(GtkWidget *window, GdkEventKey *event, cchar *file) { int ww, hh, sww, shh; int KBkey = event->keyval; if (KBkey == GDK_KEY_Escape) gtk_widget_destroy(window); if (KBkey != GDK_KEY_F11) return 1; sww = gdk_screen_get_width(zfuncs::screen); // screen dimensions shh = gdk_screen_get_height(zfuncs::screen); gtk_window_get_size(GTK_WINDOW(window),&ww,&hh); // current window dimensions if (ww < sww && hh < shh) { gtk_window_fullscreen(GTK_WINDOW(window)); // make fullscreen mode return 1; } else gtk_window_unfullscreen(GTK_WINDOW(window)); // no fullscreen mode 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 1 or more files // returns a list of filespecs (char **) terminated with null char ** zgetfiles(cchar *title, GtkWindow *parent, cchar *action, cchar *initfile, int hidden) { using namespace zfuncs; void zgetfile_preview(GtkWidget *dialog, GtkWidget *pvwidget); // private functions int zgetfile_KBkey(GtkWidget *dialog, GdkEventKey *event); 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; char *file1, *file2, **flist = 0; STATB fstat; zthreadcrash(); // thread usage not allowed 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-release-event",zgetfile_KBkey,0); // respond to F1 help key 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 5.8 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) gtk_widget_destroy(dialog); return flist; } // zgetfile private function - get preview images for image files void zgetfile_preview(GtkWidget *dialog, GtkWidget *pvwidget) { using namespace zfuncs; 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 - send F1 key (help) to main app int zgetfile_KBkey(GtkWidget *dialog, GdkEventKey *event) { int KBkey = event->keyval; if (KBkey != GDK_KEY_F1) return 0; // 5.6 KBstate(event,0); return 1; } /************************************************************************** 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) 5.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) // overhauled 5.2 { using namespace print_image; GtkPrintOperationResult printstat; GError *gerror = 0; int err; zthreadcrash(); 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; // 5.5 err = margins_setup(); // set margins and scale if (err) return; // 5.5 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 zfuncs; using namespace print_image; char printsettingsfile[200], pagesetupfile[200]; snprintf(printsettingsfile,200,"%s/printsettings",zuserdir); snprintf(pagesetupfile,200,"%s/pagesetup",zuserdir); 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 5.5 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; /*** margins top bottom left right CM [___] [___] [___] [___] Inch [___] [___] [___] [___] image scale [80|-+] percent image width height CM xx.x xx.x Inch xx.x xx.x ***/ 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,"spin","mtopcm","vbtop","0|10|0.01|0"); zdialog_add_widget(zd,"spin","mtopin","vbtop","0|4|0.01|0"); zdialog_add_widget(zd,"label","labbot","vbbottom",ZTX("bottom")); zdialog_add_widget(zd,"spin","mbottcm","vbbottom","0|10|0.01|0"); zdialog_add_widget(zd,"spin","mbottin","vbbottom","0|4|0.01|0"); zdialog_add_widget(zd,"label","lableft","vbleft",ZTX("left")); zdialog_add_widget(zd,"spin","mleftcm","vbleft","0|10|0.01|0"); zdialog_add_widget(zd,"spin","mleftin","vbleft","0|4|0.01|0"); zdialog_add_widget(zd,"label","labright","vbright",ZTX("right")); zdialog_add_widget(zd,"spin","mrightcm","vbright","0|10|0.01|0"); zdialog_add_widget(zd,"spin","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,"spin","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); // 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,"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 window drag-drop event void drag_drop_connect(GtkWidget *window, drag_drop_func *ufunc) { int drag_drop_connect2(GtkWidget *, void *, int, int, void *, int, int, void *); char string[] = "STRING"; GtkTargetEntry file_drop_target = { string, 0, 0 }; zthreadcrash(); gtk_drag_dest_set(window, GTK_DEST_DEFAULT_ALL, &file_drop_target, 1, GDK_ACTION_COPY); G_SIGNAL(window, "drag-data-received", drag_drop_connect2, ufunc); gtk_drag_dest_add_uri_targets(window); // accept URI (file) drop return; } // private function // get dropped file, clean escapes, pass to user function // passed filespec is subject for zfree() int drag_drop_connect2(GtkWidget *, void *, int mpx, int mpy, void *sdata, int, int, void *ufunc) { char * drag_drop_unescape(cchar *escaped_string); drag_drop_func *ufunc2; char *text, *text2, *file, *file2; int cc; text = (char *) gtk_selection_data_get_data((GtkSelectionData *) sdata); ufunc2 = (drag_drop_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 { text2 = zstrdup(text); ufunc2(mpx,mpy,text2); } return 1; } // 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) // 5.0 { using namespace zfuncs; PIXBUF *thumbpxb; GError *gerror = 0; int err; char *bpath; STATB statf; zthreadcrash(); // thread usage not allowed 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; // 5.8 strncatv(bpath,499,zicondir,"/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's icon directory // (see initz_appfiles()). GdkCursor * zmakecursor(cchar *iconfile) { using namespace zfuncs; GError *gerror = 0; PIXBUF *pixbuf; GdkDisplay *display; GdkCursor *cursor = 0; char iconpath[200]; zthreadcrash(); // thread usage not allowed display = gdk_display_get_default(); *iconpath = 0; strncatv(iconpath,199,zicondir,"/",iconfile,null); pixbuf = gdk_pixbuf_new_from_file(iconpath,&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; zthreadcrash(); // thread usage not allowed 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) { // bugfix 0.01 >> 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); // bugfix } 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; zthreadcrash(); // thread usage not allowed 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. 6.2 PIXBUF * text_pixbuf(cchar *text, cchar *font, int fontsize, GtkWidget *widget) { 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; 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; } /************************************************************************** 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-15.11.1/Makefile0000644000175000017500000000574712616075370013333 0ustar micomico# fotoxx makefile FOTOXX = fotoxx-15.11.1.cc # defaults for parameters that may be pre-defined CXXFLAGS += -Wall -ggdb LDFLAGS += -rdynamic PREFIX ?= /usr CPPFLAGS ?= -O2 # target install directories BINDIR = $(PREFIX)/bin SHAREDIR = $(PREFIX)/share/fotoxx DATADIR = $(SHAREDIR)/data ICONDIR = $(SHAREDIR)/icons 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` LIBS = `pkg-config --libs gtk+-3.0` -lpthread -ltiff -lpng -llcms2 ALLFILES = fotoxx.o f.widgets.o f.file.o f.gallery.o f.area.o f.meta.o \ f.edit.o f.repair.o f.bend.o f.effects.o f.combine.o f.mashup.o \ f.albums.o f.tools.o f.batch.o f.image.o zfuncs.o fotoxx: $(ALLFILES) $(CXX) $(LDFLAGS) -o fotoxx $(ALLFILES) $(LIBS) fotoxx.o: $(FOTOXX) fotoxx.h $(CXX) $(CFLAGS) -o fotoxx.o $(FOTOXX) f.widgets.o: f.widgets.cc fotoxx.h $(CXX) $(CFLAGS) f.widgets.cc f.file.o: f.file.cc fotoxx.h $(CXX) $(CFLAGS) f.file.cc f.gallery.o: f.gallery.cc fotoxx.h $(CXX) $(CFLAGS) f.gallery.cc f.area.o: f.area.cc fotoxx.h $(CXX) $(CFLAGS) f.area.cc f.meta.o: f.meta.cc fotoxx.h $(CXX) $(CFLAGS) f.meta.cc f.edit.o: f.edit.cc fotoxx.h $(CXX) $(CFLAGS) f.edit.cc f.repair.o: f.repair.cc fotoxx.h $(CXX) $(CFLAGS) f.repair.cc f.bend.o: f.bend.cc fotoxx.h $(CXX) $(CFLAGS) f.bend.cc f.effects.o: f.effects.cc fotoxx.h $(CXX) $(CFLAGS) f.effects.cc f.combine.o: f.combine.cc fotoxx.h $(CXX) $(CFLAGS) f.combine.cc f.mashup.o: f.mashup.cc fotoxx.h $(CXX) $(CFLAGS) f.mashup.cc f.albums.o: f.albums.cc fotoxx.h $(CXX) $(CFLAGS) f.albums.cc f.tools.o: f.tools.cc fotoxx.h $(CXX) $(CFLAGS) f.tools.cc f.batch.o: f.batch.cc fotoxx.h $(CXX) $(CFLAGS) f.batch.cc f.image.o: f.image.cc fotoxx.h $(CXX) $(CFLAGS) f.image.cc zfuncs.o: zfuncs.cc zfuncs.h $(CXX) $(CFLAGS) zfuncs.cc -D PREFIX=\"$(PREFIX)\" -D DOCDIR=\"$(DOCDIR)\" install: fotoxx uninstall mkdir -p $(DESTDIR)$(BINDIR) mkdir -p $(DESTDIR)$(DATADIR) mkdir -p $(DESTDIR)$(ICONDIR) 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 icons/* $(DESTDIR)$(ICONDIR) cp -f -R locales/* $(DESTDIR)$(LOCALESDIR) cp -n -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 desktop $(DESTDIR)$(MENUFILE) 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-15.11.1/f.mashup.cc0000664000175000017500000051036012616075370013715 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** Fotoxx image edit functions - Mashup function Define a layout/background image. Add images, text, and lines as overlays on top of layout. Overlays have adjustable position, angle, size, transparency. ***************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /**************************************************************************/ 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(); int add_text_dialog_event(zdialog *zd, cchar *event); void remove_text(int ii); void flashtext(); void line_edit(); int line_dialog_event(zdialog *zd, cchar *event); void add_line(); int add_line_dialog_event(zdialog *zd, cchar *event); 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; int mashup_event(zdialog *zd, cchar *event); 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,mashup_event,"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 = zgetfile(ZTX("choose layout file"),MWIN,"file",curr_file); 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,"entry","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,"spin","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,"spin","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,mashup_event); // 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; } // dialog event and completion function int mashup_event(zdialog *zd, cchar *event) { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 return 1; } // 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 KBrelease() func 15.03.1 Fpxb = PXB_copy(Lpxb); // initz. window update image 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",0,"space=3"); 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")); zdialog_add_widget(zd,"button","edittext","vb1",ZTX("Edit Text")); zdialog_add_widget(zd,"button","editline","vb1",ZTX("Edit Line")); zdialog_add_widget(zd,"button","rescale","vb1",ZTX("Rescale")); zdialog_add_widget(zd,"button","done","vb1",Bdone); zdialog_add_widget(zd,"button","cancel","vb1",Bcancel); zdialog_add_widget(zd,"label","labedim","vb2",ZTX("add or edit images")); zdialog_add_widget(zd,"label","labedtx","vb2",ZTX("add or edit text")); zdialog_add_widget(zd,"label","labedln","vb2",ZTX("add or edit lines/arrows")); zdialog_add_widget(zd,"label","labscale","vb2",ZTX("change project scale")); zdialog_add_widget(zd,"label","labdone","vb2",ZTX("project complete")); zdialog_add_widget(zd,"label","labcancel","vb2",ZTX("cancel project")); zdialog_run(zd,project_dialog_event); // 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; cchar *rescalemess = ZTX("rescale project"); if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (zd->zstat) event = "cancel"; // cancel or [x] 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 15.03 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) { // 15.04 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) { 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 15.08 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 15.03 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 15.02 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 } 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); // 15.03 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); // 15.02 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); // 15.03 } 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) // 15.03 { 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 resource_lock(Fpaintlock); 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(); } resource_unlock(Fpaintlock); 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|v] [ 1.0 ] | | Angle [12.3|v] | | Stacking Order [raise] [lower] | | Base Transparency [0.5|v] | | Variable Transparency [paint] | | Bend and fine-align [warp] | | [x] Make black margins transparent | | | | Margins hard blend | | Left [ 123|v] [ 0|v] | | Right [ 0|v] [ 123|v] | | Top [ 234|v] [ 0|v] | | Bottom [ 0|v] [ 234|v] | | | | [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,"spin","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,"spin","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,"spin","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 15.03 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,"spin","Lmarg","vbmarg2","0|999|1|0"); zdialog_add_widget(zd,"spin","Rmarg","vbmarg2","0|999|1|0"); zdialog_add_widget(zd,"spin","Tmarg","vbmarg2","0|999|1|0"); zdialog_add_widget(zd,"spin","Bmarg","vbmarg2","0|999|1|0"); zdialog_add_widget(zd,"spin","Lblend","vbmarg3","0|999|1|0"); zdialog_add_widget(zd,"spin","Rblend","vbmarg3","0|999|1|0"); zdialog_add_widget(zd,"spin","Tblend","vbmarg3","0|999|1|0"); zdialog_add_widget(zd,"spin","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,"escape")) event = "kill"; // escape = cancel 15.07 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 15.03 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 15.03 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 15.03 if (strmatch(event,"Lmarg")) // left edge margin 15.03 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 15.02 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] = 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|^] Gradual [x] | | 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,"spin","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,"vbox","vbpow1","hbpow",0,"space=3"); zdialog_add_widget(zdtransp,"vbox","vbpow2","hbpow",0,"space=3"); zdialog_add_widget(zdtransp,"vbox","vbpow3","hbpow",0,"space=3"); zdialog_add_widget(zdtransp,"label","labpower","vbpow1",ZTX("Power"),"space=5"); zdialog_add_widget(zdtransp,"label","labcen","vbpow2",Bcenter,"space=5"); zdialog_add_widget(zdtransp,"spin","center","vbpow3","0|100|1|10"); zdialog_add_widget(zdtransp,"label","labedge","vbpow2",Bedge,"space=5"); zdialog_add_widget(zdtransp,"spin","edge","vbpow3","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 (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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); // 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); // 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); // 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); // no mouse circle gdk_window_set_cursor(gdkwin,0); return; } draw_mousecircle(mx,my,mrad,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 15.02 } 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() // 15.03 { 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,"spin","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 (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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 memory 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] = 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() // 15.03 { 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; int ww1, hh1, ww2, hh2; float scale; char **selfiles, *file; PXB *pxb1; zdialog_show(zdimage,0); // hide parent dialog selfiles = gallery_getfiles(null); // select image files from gallery m_viewmode(0,"F"); // restore view mode F zdialog_show(zdimage,1); if (! selfiles) return; // no files selected for (nn = 0; selfiles[nn]; nn++); // count files selected Nfiles = nn; 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 15.08 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); 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); // 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|v] | | | | color transp. width angle | | text [_____] [___|v] [___|v] | | backing [_____] [___|v] | | outline [_____] [___|v] [___|v] | | shadow [_____] [___|v] [___|v] [___|v] | | | | Text File: [Open] [Save] | | | | [Add] [Delete] [Done] | |_____________________________________________________| ***/ if (! strmatch(focus,"text")) select("",-1); // nothing selected zdtext = zdialog_new(ZTX("Edit Text"),Mwin,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=8"); zdialog_add_widget(zd,"button","fontbutt","hbfont",Bfont); zdialog_add_widget(zd,"entry","fontname","hbfont","FreeSans","space=2|expand"); zdialog_add_widget(zd,"label","labsize","hbfont",Bsize,"space=2"); zdialog_add_widget(zd,"spin","fontsize","hbfont","8|500|1|40"); 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","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",Btransparency); zdialog_add_widget(zd,"spin","fgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"spin","bgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"spin","totransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"spin","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,"spin","towidth","vbcol4","0|30|1|0"); zdialog_add_widget(zd,"spin","shwidth","vbcol4","0|50|1|0"); zdialog_add_widget(zd,"label","labw","vbcol5",Bangle); zdialog_add_widget(zd,"spin","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,"spin","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,"escape")) event = "kill"; // escape = cancel 15.07 if (strmatch(event,"kill")) { // kill dialog zdialog_free(zd); zdtext = 0; return 1; } if (zd->zstat) { if (zd->zstat == 1) { // add zd->zstat = 0; add_text(); // zdialog inputs >> new text image return 1; } if (zd->zstat == 2) { // 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(0,Mwin,Bcancel,null); // get mouse click for text position 15.06 zdialog_set_decorated(zdaddtext,0); // 15.07 zdialog_add_widget(zdaddtext,"label","labtip","dialog",tip,"space=3"); zdialog_run(zdaddtext,add_text_dialog_event,"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; } // dialog event and completion function int mashup::add_text_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 return 1; } // 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 [_____] [___|v] [___|v] | | backing [_____] [___|v] | | outline [_____] [___|v] [___|v] | | shadow [_____] [___|v] [___|v] [___|v] | | | | [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,"spin","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,"spin","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",Btransparency); zdialog_add_widget(zd,"spin","fgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"spin","bgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"spin","totransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"spin","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,"spin","towidth","vbcol4","0|30|1|0"); zdialog_add_widget(zd,"spin","shwidth","vbcol4","0|50|1|0"); zdialog_add_widget(zd,"label","labw","vbcol5",Bangle); zdialog_add_widget(zd,"spin","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,"spin","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,"escape")) event = "kill"; // escape = cancel 15.07 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(0,Mwin,Bdone,Bcancel,null); // get mouse click for line position 15.06 zdialog_set_decorated(zdaddline,0); // 15.07 zdialog_add_widget(zdaddline,"label","labtip","dialog",tip,"space=3"); zdialog_run(zdaddline,add_line_dialog_event,"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; } // dialog event and completion function int mashup::add_line_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 return 1; } // 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); // 15.03 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); // 15.02 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 15.03 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; // 15.03 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 15.08 } 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 15.03 Fnextimage = 0; Fnew = 0; Mcen = 1; iiMcen = focusii; } if (zdaddtext && (Mxclick + Myclick)) { // return clicked position 15.06 zdaddtext->zstat = 1; zdialog_destroy(zdaddtext); return; } if (zdaddline && (Mxclick + Myclick)) { // return clicked position 15.06 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) { // 15.03 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 15.03 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 15.03 if (my0 < image[ii].pylo || my0 > image[ii].pyhi) return; select("image",ii); pxlo = image[ii].pxlo; // image extent bugfix 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 15.03 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 15.03 if (my0 < text[ii].pylo || my0 > text[ii].pyhi) return; select("text",ii); pxlo = text[ii].pxlo; // text image extent bugfix 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 15.03 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 15.03 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); // 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 15.08 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 15.03 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; // 15.08 green = Iopac * vpix2[1] + (1 - Iopac) * green; blue = Iopac * vpix2[2] + (1 - Iopac) * blue; } for (ii = 0; ii < Ntext; ii++) // loop overlay text images 15.08 { 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 15.08 { 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 } fotoxx-15.11.1/f.effects.cc0000664000175000017500000062327312616075370014047 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** Fotoxx image edit - Effects (filters) menu functions m_colordep reduce color depth from 16 ... 1 bits m_sketch convert photo to simulated pencil sketch m_linedraw combine image and highlighted edge pixels m_colordraw convert image into high-contrast color drawing m_gradblur 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_waves distort an image with a wave pattern m_dirblur directed 1-dimensional blur using the mouse m_sphere spherical image projection, variable radius ***************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /**************************************************************************/ // image color-depth reduction int colordep_depth = 16; // bits per RGB color editfunc EFcolordep; void m_colordep(GtkWidget *, cchar *) { 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.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.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,"spin","colors","hb2","1|16|1|16","space=5"); colordep_depth = 16; zdialog_resize(zd,250,0); zdialog_run(zd,colordep_dialog_event,"-10/20"); // run dialog, parallel return; } // dialog event and completion callback function int colordep_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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 = 1.0 * dist / sa_blend; // 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 } /**************************************************************************/ // sketch menu function - convert photo to simulated pencil 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 *) // overhauled { 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 = "pencil_sketch"; EFsketch.menufunc = m_sketch; EFsketch.funcname = "sketch"; // function name EFsketch.Farea = 2; // select area usable 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,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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; } if (strmatch(event,"Bweight")) { zdialog_fetch(zd,"Bweight",Bweight); // revised brightness weight signal_thread(); // trigger update thread } if (strmatch(event,"Brthresh")) { zdialog_fetch(zd,"Brthresh",Brthresh); // brightness threshold signal_thread(); } if (strmatch(event,"Cweight")) { zdialog_fetch(zd,"Cweight",Cweight); // contrast weight signal_thread(); } if (strmatch(event,"cliplev")) { // output clip level zdialog_fetch(zd,"cliplev",cliplev); cliplev = 255 - cliplev; // scale is reversed signal_thread(); } if (strstr(event,"algorithm")) { // algorithm to use zdialog_fetch(zd,"algorithm1",ii); if (ii == 1) algorithm = 1; else algorithm = 2; signal_thread(); } if (strmatch(event,"fgcolor")) { // foreground color 15.07 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); signal_thread(); } if (strmatch(event,"bgcolor")) { // background color 15.07 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 = 1.0 * dist / sa_blend; 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; } /**************************************************************************/ // 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_linedraw(GtkWidget *, cchar *) { using namespace linedraw_names; int linedraw_dialog_event(zdialog* zd, cchar *event); void * linedraw_thread(void *); F1_help_topic = "line_drawing"; EFlinedraw.menufunc = m_linedraw; EFlinedraw.funcname = "linedraw"; EFlinedraw.Farea = 2; // select area usable EFlinedraw.threadfunc = linedraw_thread; // thread function if (! edit_setup(EFlinedraw)) return; // setup edit 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,"-10/20"); // 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,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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; } if (strmatch(event,"olth")) { zdialog_fetch(zd,"olth",outline_thresh); // get outline threshold 0-100 signal_thread(); } if (strmatch(event,"olww")) { zdialog_fetch(zd,"olww",outline_width); // get outline width 0-100 signal_thread(); } if (strmatch(event,"imbr")) { zdialog_fetch(zd,"imbr",image_briteness); // get image brightness 0-100 signal_thread(); } if (strmatch(event,"blackwhite")) { zdialog_fetch(zd,"blackwhite",blackwhite); signal_thread(); } if (strmatch(event,"negative")) { zdialog_fetch(zd,"negative",negative); signal_thread(); } if (strmatch(event,"undo")) edit_undo(); if (strmatch(event,"redo")) edit_redo(); if (strmatch(event,"blendwidth")) 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 15.09 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 = 1.0 * dist / sa_blend; // 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 } void m_colordraw(GtkWidget *, cchar *) // menu function { using namespace colordraw_names; int colordraw_dialog_event(zdialog *zd, cchar *event); void * colordraw_thread(void *); F1_help_topic = "color_drawing"; EFcolordraw.menufunc = m_colordraw; EFcolordraw.funcname = "colordraw"; EFcolordraw.Farea = 2; // select area usable EFcolordraw.Frestart = 1; // allow restart 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,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"threshx")) { // dark/bright threshold adjustment zdialog_fetch(zd,"threshx",threshx); signal_thread(); } if (strmatch(event,"darkx")) { // dark level adjustment zdialog_fetch(zd,"darkx",darkx); signal_thread(); } if (strmatch(event,"britex")) { // bright level adjustment zdialog_fetch(zd,"britex",britex); signal_thread(); } if (strmatch(event,"blendwidth")) 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 = 1.0 * dist / sa_blend; // 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; int gradblur_cancel; uint8 *pixcon; 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_gradblur(GtkWidget *, cchar *) { 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.menufunc = m_gradblur; EFgradblur.funcname = "gradblur"; EFgradblur.Farea = 2; // select area usable 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,"spin","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,"spin","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,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 2; // KB input if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 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 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 = 1.0 * dist / 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]; } 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; void m_emboss(GtkWidget *, cchar *) { int emboss_dialog_event(zdialog* zd, cchar *event); void * emboss_thread(void *); cchar *title = ZTX("Simulate Embossing"); F1_help_topic = "embossing"; EFemboss.menufunc = m_emboss; EFemboss.funcname = "emboss"; EFemboss.Farea = 2; // select area usable EFemboss.threadfunc = emboss_thread; // thread function if (! edit_setup(EFemboss)) return; // setup edit 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,"spin","radius","hb1","0|9|1|0"); zdialog_add_widget(zd,"label","lab2","hb1",ZTX("depth"),"space=5"); zdialog_add_widget(zd,"spin","depth","hb1","0|99|1|0"); zdialog_add_widget(zd,"check","color","hb1",Bcolor,"space=8"); zdialog_run(zd,emboss_dialog_event,"-10/20"); // 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,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (zd->zstat) { if (zd->zstat == 1) 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 (strmatchV(event,"radius","depth","color",null)) { 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; // 15.09 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 = 1.0 * dist / sa_blend; // 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; // 15.09 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 = 1.0 * dist / sa_blend; // 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 *) { int tiles_dialog_event(zdialog *zd, cchar *event); void * tiles_thread(void *); F1_help_topic = "tiles"; EFtiles.menufunc = m_tiles; EFtiles.funcname = "tiles"; EFtiles.Farea = 2; // select area usable EFtiles.threadfunc = tiles_thread; // thread function if (! edit_setup(EFtiles)) return; // setup edit 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,"spin","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,"spin","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,"spin","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,"-10/20"); // run dialog, parallel return; } // dialog event and completion callback function int tiles_dialog_event(zdialog * zd, cchar *event) { if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 2; // KB input if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 if (zd->zstat && zd->zstat != 1) // dialog complete { if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit zfree(tile_pixmap); return 1; } if (strmatch(event,"blendwidth")) signal_thread(); if (zd->zstat != 1) return 0; // 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 return 1; } signal_thread(); // trigger working thread 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 = 1.0 * dist / sa_blend; 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 *) { int dots_dialog_event(zdialog *zd, cchar *event); void * dots_thread(void *); F1_help_topic = "dot_matrix"; EFdots.menufunc = m_dots; EFdots.funcname = "dots"; EFdots.Farea = 2; // select area usable EFdots.threadfunc = dots_thread; // thread function if (! edit_setup(EFdots)) return; // setup edit zdialog *zd = zdialog_new(ZTX("Convert Image to Dots"),Mwin,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,"spin","size","hb1","4|99|1|9","space=5"); zdialog_add_widget(zd,"button","apply","hb1",Bapply,"space=10"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); dot_size = 9; zdialog_run(zd,dots_dialog_event,"-10/20"); // run dialog, parallel return; } // dialog event and completion callback function int dots_dialog_event(zdialog * zd, cchar *event) { if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (zd->zstat) { if (zd->zstat == 1) 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,"apply")) return 0; // wait for [apply] zdialog_fetch(zd,"size",dot_size); // get dot size signal_thread(); // trigger working thread 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 if (! resource_lock(Fpaintlock)) continue; // 15.02 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 15.09 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 = 1.0 * dist / sa_blend; // 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; // 15.09 pix3 += nc; } } resource_unlock(Fpaintlock); 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 *) { using namespace painting_names; int painting_dialog_event(zdialog *zd, cchar *event); void * painting_thread(void *); F1_help_topic = "painting"; EFpainting.menufunc = m_painting; EFpainting.funcname = "painting"; EFpainting.Farea = 2; // select area usable EFpainting.threadfunc = painting_thread; // thread function if (! edit_setup(EFpainting)) return; // setup edit zdialog *zd = zdialog_new(ZTX("Simulate Painting"),Mwin,Bdone,Bcancel,null); EFpainting.zd = zd; 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,"spin","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,"spin","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,"spin","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_add_widget(zd,"button","apply","hbbd",Bapply,"space=10"); 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,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (zd->zstat) { if (zd->zstat == 1) 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,"apply")) { // apply user settings 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 } 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 = 1.0 * dist / sa_blend; // 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,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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; // 15.10 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 = 1.0 * dist / sa_blend; // 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 *, const char *) { using namespace texturenames; int texture_dialog_event(zdialog* zd, const char *event); void * texture_thread(void *); zdialog *zd; F1_help_topic = "texture"; EFtexture.menufunc = m_texture; EFtexture.funcname = "texture"; // function name EFtexture.Farea = 2; // select area OK 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,"spin","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,"spin","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,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 2; // KB input if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 if (zd->zstat) { if (zd->zstat == 1) { // apply zd->zstat = 0; // keep dialog active edit_reset(); signal_thread(); // process return 1; } if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"radius")) // get radius zdialog_fetch(zd,"radius",radius); if (strmatch(event,"power")) // get power zdialog_fetch(zd,"power",power); 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 = 1.0 * dist / sa_blend; // 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; } void m_pattern(GtkWidget *, const char *) { using namespace patternnames; int pattern_dialog_event(zdialog* zd, const char *event); void * pattern_thread(void *); zdialog *zd; F1_help_topic = "pattern"; EFpattern.menufunc = m_pattern; EFpattern.funcname = "pattern"; // function name EFpattern.Farea = 2; // select area OK *** fixme *** 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 [___|-+] Height [___|-+] | | 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,"entry","pattfile","hbfile",0,"expand"); zdialog_add_widget(zd,"button","browse","hbfile",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hbcalc","dialog"); zdialog_add_widget(zd,"label","labzoom","hbcalc",ZTX("Zoom"),"space=5"); zdialog_add_widget(zd,"spin","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","hbsize","dialog"); zdialog_add_widget(zd,"vbox","vbs1","hbsize",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vbs2","hbsize",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","space","hbsize",0,"space=8"); zdialog_add_widget(zd,"vbox","vbs3","hbsize",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vbs4","hbsize",0,"space=3|homog"); zdialog_add_widget(zd,"hbox","hbs11","vbs1"); 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,"spin","pattww","vbs2","20|1000|1|100"); zdialog_add_widget(zd,"label","labheight","vbs3",Bheight,"space=3"); zdialog_add_widget(zd,"spin","patthh","vbs4","20|1000|1|100"); zdialog_add_widget(zd,"hbox","hbs12","vbs1"); 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,"spin","olapww","vbs2","0|1000|1|0"); zdialog_add_widget(zd,"label","labheight","vbs3",Bheight,"space=3"); zdialog_add_widget(zd,"spin","olaphh","vbs4","0|1000|1|0"); zdialog_add_widget(zd,"hbox","hbopac","dialog"); zdialog_add_widget(zd,"label","labopac","hbopac",ZTX("Opacity"),"space=5"); zdialog_add_widget(zd,"spin","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,"spin","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 15.11 { using namespace patternnames; void pattern_match(); char temp1[150], temp2[200]; char *file, *pp; PIXBUF *pixbuf2; GError *gerror = 0; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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,"pattww",pattww); // fetch all dialog parameters zdialog_fetch(zd,"patthh",patthh); 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 = zgetfile(ZTX("choose pattern tile"),MWIN,"file",pp); 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 shell_quiet("cp \"%s\" \"%s\" ",pattfile,pattern_dirk); pixbuf2 = gdk_pixbuf_stripalpha(pixbuf); // strip alpha channel if present 15.08 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 if (strmatch(event,"browse")) { 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 15.08 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); // rescale pixbuf 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 (strmatch(event,"init")) { zdialog_fetch(zd,"pattww",pattww); // leave unchanged if "init" zdialog_fetch(zd,"patthh",patthh); } } if (olapww > pattww/3) olapww = pattww/3; // prevent nonsense 15.11 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 pattww patthh olapww olaphh opacity contrast",event)) { update_thread++; signal_thread(); // update image } 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) { update_thread = 0; start_wthread(pattern_wthread,&Nval[0]); // start worker thread (1) wait_wthreads(); // wait for completion } 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 15.11 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 15.11 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 15.11 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 = 1.0 * dist / sa_blend; // 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 int Nfiles, Ntiles; // tile file count, tile image count 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) 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 char tilesavefile[200]; int mosaic_dialog_event(zdialog*, cchar *); void mosaic_mousefunc(); char **fif_filelist; // find_imagefiles() variables int fif_count1; int fif_count2; } // menu function void m_mosaic(GtkWidget *, const char *) { using namespace mosaic_names; cchar *title = ZTX("Create Mosaic"); zdialog *zd; int ii, cc, nfid, cc1, cc2; char label[12]; 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; Nfiles = 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); snprintf(tilesavefile,200,"%s/mosaic_tiles",get_zuserdir()); // read tile images from save file nfid = open(tilesavefile,O_RDONLY); if (nfid >= 0) { cc1 = sizeof(int); cc2 = read(nfid,&Ntiles,cc1); // read tile count, width, height cc2 = read(nfid,&tww,cc1); cc2 = read(nfid,&thh,cc1); for (ii = 0; ii < Ntiles; ii++) // read tile images { cc2 = read(nfid,&cc1,sizeof(int)); tilefile[ii] = (char *) zmalloc(cc1); cc2 = read(nfid,tilefile[ii],cc1); // tile filespec if (cc2 != cc1) break; cc1 = tww * thh * 3; tileimage[ii] = (uint8 *) zmalloc(cc1); cc2 = read(nfid,tileimage[ii],cc1); // tile image if (cc2 != cc1) break; cc1 = 3 * sizeof(float); cc2 = read(nfid,&tileRGB1[ii],cc1); // tile RGB, each quadrant cc2 = read(nfid,&tileRGB2[ii],cc1); cc2 = read(nfid,&tileRGB3[ii],cc1); cc2 = read(nfid,&tileRGB4[ii],cc1); if (cc2 != cc1) break; } close(nfid); if (cc2 != cc1) { // file error zmessageACK(Mwin,"cannot read tile save file"); tww = 32; // reset default tile size thh = 24; Nfiles = Ntiles = 0; // tile file and image counts } } /*** _______________________________________________ | 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,"spin","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,"spin","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",ZTX("Image"),"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_names::mosaic_dialog_event(zdialog *zd, const char *event) { using namespace mosaic_names; PIXBUF *pxb1, *pxb2; GError *gerror = 0; static char **flist = 0; char label[12]; int ii, jj, err, NF; int trow, tcol, tpx, tpy, ipx, ipy; int tww1, thh1, tww2, thh2; int rs, bestii, blend; uint8 *tpixels, *tpix, *timage; uint8 *row1, *row2; float *pix1, *pix3; float fred1, fgreen1, fblue1; float fred2, fgreen2, fblue2; float fred3, fgreen3, fblue3; float fred4, fgreen4, fblue4; float match, bestmatch; float f1, f3, coeff; int nfid, cc1, cc2; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit for (ii = 0; ii < Nfiles; ii++) // free memory zfree(tilefile[ii]); for (ii = 0; ii < Ntiles; ii++) zfree(tileimage[ii]); if (tilemap) zfree(tilemap); return 1; } if (strmatch(event,"focus")) takeMouse(mosaic_mousefunc,dragcursor); // connect mouse function if (strmatch(event,"tiles")) // read thumbnails, create tile images { for (ii = 0; ii < Nfiles; ii++) // free prior memory zfree(tilefile[ii]); for (ii = 0; ii < Ntiles; ii++) zfree(tileimage[ii]); Nfiles = Ntiles = 0; zdialog_stuff(zd,"labNtiles","0"); err = find_imagefiles(thumbdirk,flist,NF,1); // find all thumbnail image files if (err) { zmessageACK(Mwin,strerror(errno)); return 1; } if (NF > maxtiles) { // too many zmessageACK(Mwin,ZTX("exceeded max. tiles: %d"),maxtiles); zfree(flist); flist = 0; return 1; } for (ii = 0; ii < NF; ii++) // save thumbnail filespecs tilefile[ii] = flist[ii]; Nfiles = NF; zfree(flist); flist = 0; if (Nfiles < 100) { zmessageACK(Mwin,ZTX("only %d tile images found"),Nfiles); return 1; } for (ii = 0; ii < Ntiles; ii++) // free tile image memory zfree(tileimage[ii]); zdialog_fetch(zd,"width",tww); // get tile size from dialog zdialog_fetch(zd,"height",thh); for (Ntiles = ii = 0; ii < Nfiles; ii++) // loop tile image files { pxb1 = gdk_pixbuf_new_from_file_at_size // create pixbuf fitting within 64x64 (tilefile[ii],64,64,&gerror); 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 // rescale for max. tww x thh section (tilefile[ii],tww2,thh2,&gerror); // (tww2 = tww and thh2 >= thh if (! pxb2) { // or thh2 = thh and tww2 >= tww) 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); } g_object_unref(pxb2); tileimage[Ntiles] = timage; // save final tile image Ntiles++; snprintf(label,12,"%d",Ntiles); // show count in dialog zdialog_stuff(zd,"labNtiles",label); zmainloop(); } for (ii = 0; ii < Ntiles; ii++) // loop tile images { timage = tileimage[ii]; 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; } nfid = open(tilesavefile,O_WRONLY|O_CREAT|O_TRUNC,0640); // save tile images to file if (nfid < 0) { zmessageACK(Mwin,"%s \n %s",tilesavefile,strerror(errno)); return 1; } cc1 = sizeof(int); // write tile count cc2 = write(nfid,&Ntiles,cc1); cc2 = write(nfid,&tww,cc1); cc2 = write(nfid,&thh,cc1); for (ii = 0; ii < Ntiles; ii++) // write tile images { cc1 = strlen(tilefile[ii]) + 1; cc2 = write(nfid,&cc1,sizeof(int)); cc2 = write(nfid,tilefile[ii],cc1); // tile file + trailing 0 if (cc2 != cc1) break; cc1 = tww * thh * 3; cc2 = write(nfid,tileimage[ii],cc1); // tile image if (cc2 != cc1) break; cc1 = 3 * sizeof(float); cc2 = write(nfid,&tileRGB1[ii],cc1); // tile RGB, each quadrant cc2 = write(nfid,&tileRGB2[ii],cc1); cc2 = write(nfid,&tileRGB3[ii],cc1); cc2 = write(nfid,&tileRGB4[ii],cc1); if (cc2 != cc1) break; } close(nfid); if (cc2 != cc1) zmessageACK(Mwin,"cannot write tile save file"); } if (strmatch(event,"image")) { if (! Ntiles) return 1; edit_undo(); // reset to original image 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++) { 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 { 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] = 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]; ii = iww * ipy + ipx; // save map of tile used tilemap[ii] = bestii; // at each image pixel } Fpaint2(); // update image for each tile zmainloop(); // (actually periodic) } CEF->Fmods++; // image is modified CEF->Fsaved = 0; } return 1; } // mouse function - called for mouse events inside image void mosaic_names::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 { zdialog *zd = 0; int ww, hh; // image dimensions int kernsize = 5; // kernel size, N x N int divisor = 1; // kernel divisor int kernel[15][15]; // up to 15 x 15 editfunc EFanykernel; } void m_anykernel(GtkWidget *, cchar *) // menu function { using namespace anykernel_names; int anykernel_make_dialog(); void * anykernel_thread(void *); F1_help_topic = "custom_kernel"; EFanykernel.menufunc = m_anykernel; EFanykernel.funcname = "custom_kernel"; EFanykernel.Farea = 2; // select area usable 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() // 15.10 { using namespace anykernel_names; int anykernel_dialog_event(zdialog *zd, cchar *event); int row, col; char rowname[12], cellname[12]; /*** __________________________________________ | Custom Kernel | | | | Kernel size [___|+-] Divisor [____] | | Data file [Load] [Save] | | | | [____] [____] [____] [____] [____] ... | | [____] [____] [____] [____] [____] ... | | [____] [____] [____] [____] [____] ... | | [____] [____] [____] [____] [____] ... | | [____] [____] [____] [____] [____] ... | | [____] [____] [____] [____] [____] ... | | ... | | | | [Reset] [Apply] [Done] [Cancel] | |__________________________________________| ***/ if (zd) zdialog_free(zd); 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,"spin","kernsize","hbkern","3|15|2|5"); zdialog_add_widget(zd,"label","space","hbkern",0,"space=5"); zdialog_add_widget(zd,"label","labdiv","hbkern",ZTX("Divisor"),"space=3"); zdialog_add_widget(zd,"entry","divisor","hbkern","1","size=3"); zdialog_add_widget(zd,"hbox","hbfile","dialog"); 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_add_widget(zd,"vbox","space","dialog",0,"space=5"); for (row = 1; row <= kernsize; row++) { snprintf(rowname,12,"row%02d",row); zdialog_add_widget(zd,"hbox",rowname,"dialog","space=5"); for (col = 1; col <= kernsize; col++) { snprintf(cellname,12,"cell%02d%02d",col,row); zdialog_add_widget(zd,"entry",cellname,rowname,"0","size=3|space=5"); } } zdialog_stuff(zd,"kernsize",kernsize); // stuff prior values zdialog_stuff(zd,"divisor",divisor); 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[12]; char *filename, dirname[200]; FILE *fid; cchar *mess = ZTX("Load settings from file"); if (strmatch(event,"enter")) zd->zstat = 2; // Apply 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,"escape")) zd->zstat = 4; // escape = cancel 15.07 if (zd->zstat) { if (zd->zstat == 1) // reset 15.10 { 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,12,"cell%02d%02d",col,row); zdialog_fetch(zd,cellname,value); kernel[row-1][col-1] = value; } zdialog_fetch(zd,"divisor",divisor); signal_thread(); // start thread return 1; } if (zd->zstat == 3) // done - commit edit { edit_save_last_widgets(&EFanykernel); // 15.10 edit_done(0); } else edit_cancel(0); // cancel - discard edit return 1; } if (strmatch(event,"kernsize")) // change kernel size { zdialog_fetch(zd,"kernsize",kernsize); zd->zstat = 3; 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 15.10 { snprintf(dirname,200,"%s/%s",get_zuserdir(),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 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); 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 15.10 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 (divisor) { // divide sum by divisor f1 = 1.0 / divisor; red = f1 * red; green = f1 * green; blue = f1 * blue; } 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 = 1.0 * dist / sa_blend; // 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 } /**************************************************************************/ // 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 } 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,"spin","wlh","vbw2","3|500|1|50","expand"); zdialog_add_widget(zd,"spin","amph","vbw2","0|100|1|20","expand"); zdialog_add_widget(zd,"spin","varh","vbw2","0|100|1|20","expand"); zdialog_add_widget(zd,"label","labh","vbw3",ZTX("vertical"),"space=1"); zdialog_add_widget(zd,"spin","wlv","vbw3","3|500|1|50","expand"); zdialog_add_widget(zd,"spin","ampv","vbw3","0|100|1|20","expand"); zdialog_add_widget(zd,"spin","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,"spin","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,"enter")) zd->zstat = 2; // KB input if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 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 = 1.0 * edist / sa_blend; 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 } /**************************************************************************/ // 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_dirblur(GtkWidget *, cchar *) // 15.04 { using namespace dirblur_names; cchar *dirblur_tip = ZTX("Pull image using the mouse."); F1_help_topic = "directed_blur"; EFdirblur.menufunc = m_dirblur; 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,"spin","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,"spin","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,"enter")) zd->zstat = 2; // KB input if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 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 } /**************************************************************************/ // 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 *) // 15.11 { 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,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 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); 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); } exit_wthread(); // exit thread return 0; // not executed, avoid gcc warning } fotoxx-15.11.1/f.combine.cc0000664000175000017500000100663512616075370014042 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** Fotoxx image edit functions - Combine menu functions cim_load_files load image all files into pixmaps cim_scale_image scale image to cimScale <= 1.0 cim_get_overlap get overlap area for two curved images cim_match_colors use overlap area to calibrate color differences cim_adjust_colors modify images to make the colors match cim_get_redpix find high-contrast pixels in overlap area cim_curve_image curve image for panorama stitching cim_warp_image warp image corners to improve alignment cim_align_image optimize alignment offsets and corner warps cim_match_images compute level of image alignment cim_show_images show all images aligned and combined cim_trim cut off unmatched edges of aligned images m_HDR combine and adjust images with different exposures m_HDF combine and adjust images with different focus depth m_STP combine multiple photos to eliminate transient objects m_STN combine multiple photos to reduce noise m_pano combine overlapped images horizontally m_vpano combine overlapped images vertically m_pano_PT combine overlapped images using Panorama Tools ***************************************************************************/ #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 char *cimFile[10]; // 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 15.09 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 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_dump_offsets(cchar *label); // diagnostic tool /**************************************************************************/ // load image files into pixmaps cimPXMf[*] and check for errors // returns 0 if error int cim_load_files() { PXM *pxm; for (int imx = 0; imx < cimNF; imx++) { PXM_free(cimPXMf[imx]); pxm = PXM_load(cimFile[imx],1); // ACK errors if (! pxm) return 0; PXM_addalpha(pxm); // 15.09 cimPXMf[imx] = pxm; } 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 15.09 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 15.09 ++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 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 15.09 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 15.01 { 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; } // unbend the combined panorama image void cim_uncurve_image() // 15.01 { PXM *pxmout; int ww, hh, vstat; float ww2, hh2; int px, py; int rx, ry; float dx, dy; float sx, sy; float tx, ty; float F = cimPanoFL; float *pix, vpix[4]; float *pix1, *pix2; ww = E3pxm->ww; hh = E3pxm->hh; pxmout = PXM_make(1.4*ww,hh,4); for (ry = 0; ry < hh; ry++) for (rx = 0; rx < ww; rx++) { px = rx + 0.2 * ww; py = ry; pix1 = PXMpix(E3pxm,rx,ry); pix2 = PXMpix(pxmout,px,py); memcpy(pix2,pix1,pixcc); } PXM_free(E3pxm); E3pxm = pxmout; ww = E3pxm->ww; hh = E3pxm->hh; pxmout = PXM_make(ww,hh,4); ww2 = ww/2; hh2 = hh/2; for (py = 0; py < hh; py++) for (px = 0; px < ww; px++) { dx = px - ww2; tx = atanf(dx / F); sx = F * tx; sx += ww2; dy = py - hh2; ty = atanf(dy / F * cosf(tx)); sy = F * ty; sy += hh2; 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; } // 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; 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) // do auto corner warping { 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); } if (cim_manualwarp) continue; // skip auto warp // 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 15.09 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 15.01 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 15.09 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 15.01 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 15.09 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 15.01 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; if (Fpaintlock) return; if (! resource_lock(Fpaintlock)) return; // 15.03 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; } } } resource_unlock(Fpaintlock); 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 15.09 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] < 254) continue; // voided pixel 15.09 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; if (Fpaintlock) return; if (! resource_lock(Fpaintlock)) return; // 15.03 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; } } } resource_unlock(Fpaintlock); 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 15.09 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 15.09 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] < 254) continue; // voided pixel 15.09 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; } // 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; } /************************************************************************** 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 HDRstat; // 1 = OK, 0 = failed or canceled float HDRinitAlignSize = 160; // initial align image size float HDRimageIncrease = 1.6; // image size increase per align cycle float HDRsampSize = 6000; // pixel sample size 11.03 float HDRinitSearchRange = 8.0; // initial search range, +/- pixels float HDRinitSearchStep = 1.0; // initial search step, pixels float HDRinitWarpRange = 3.0; // initial corner warp range, +/- pixels float HDRinitWarpStep = 0.67; // initial corner warp step, pixels float HDRsearchRange = 2.0; // normal search range, +/- pixels float HDRsearchStep = 0.67; // normal search step, pixels float HDRwarpRange = 2.0; // normal corner warp range, +/- pixels float HDRwarpStep = 0.67; // normal corner warp step, pixels float *HDRbright = 0; // maps brightness per pixel zdialog *HDRadjustzd = 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 **flist, *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; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } cimNF = 0; HDRbright = 0; cim_manualwarp = 0; flist = gallery_getfiles(); // select images to combine if (! flist) goto cleanup; for (imx = 0; flist[imx]; imx++); // count selected files if (imx < 2 || imx > 9) { zmessageACK(Mwin,ZTX("Select 2 to 9 files")); goto cleanup; } cimNF = imx; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(flist[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[cimNF-1]); // curr_file = last in list 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; // bugfix 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 (HDRstat != 1) goto cancel; HDR_brightness(); // compute pixel brightness levels if (HDRstat != 1) goto cancel; HDR_adjust(); // combine images based on user inputs if (HDRstat != 1) goto cancel; CEF->Fmods++; // done CEF->Fsaved = 0; edit_done(0); goto cleanup; cancel: edit_cancel(0); cleanup: if (flist) { for (imx = 0; flist[imx]; imx++) // free file list zfree(flist[imx]); zfree(flist); } 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 (HDRbright) zfree(HDRbright); *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 = HDRinitAlignSize / 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 = HDRinitSearchRange; // initial align search range cimSearchStep = HDRinitSearchStep; // initial align search step cimWarpRange = HDRinitWarpRange; // initial align corner warp range cimWarpStep = HDRinitWarpStep; // initial align corner warp step cimSampSize = HDRsampSize; // 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 = HDRimageIncrease; // 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 = HDRsearchRange; // align search range cimSearchStep = HDRsearchStep; // align search step size cimWarpRange = HDRwarpRange; // align corner warp range cimWarpStep = HDRwarpStep; // 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; HDRstat = 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; HDRbright = (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 15.09 kk = py3 * ww + px3; HDRbright[kk] = 0; continue; } bright = (red + green + blue) / (3 * cimNF); // mean pixel brightness, 0.0 to 1.0 kk = py3 * ww + px3; HDRbright[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++) HDRbright[ii] = (HDRbright[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); HDRadjustzd = zdialog_new(ZTX("Adjust Image Contributions"),Mwin,Bdone,Bcancel,null); zdialog *zd = HDRadjustzd; 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,"-10/20"); // 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,"escape")) zd->zstat = 2; // escape = cancel 15.07 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] 15.02 wrapup_thread(8); if (zd->zstat == 1) HDRstat = 1; else HDRstat = 0; if (HDRstat == 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(HDRadjustzd,"labf2",pp+1); signal_thread(); return; } // Combine all input images >> E3pxm based on image response curves. void * HDR_adjust_thread(void *) { void * HDR_combine_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_combine_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fpaint2(); // update window } return 0; // not executed } void * HDR_combine_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 = HDRbright[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 HDFstat; // 1 = OK, 0 = failed or canceled float HDFinitAlignSize = 160; // initial align image size float HDFimageIncrease = 1.6; // image size increase per align cycle float HDFsampSize = 6000; // pixel sample size float HDFinitSearchRange = 8.0; // initial search range, +/- pixels float HDFinitSearchStep = 1.0; // initial search step, pixels float HDFinitWarpRange = 4.0; // initial corner warp range float HDFinitWarpStep = 1.0; // initial corner warp step float HDFsearchRange = 2.0; // normal search range float HDFsearchStep = 1.0; // normal search step float HDFwarpRange = 1.0; // normal corner warp range float HDFwarpStep = 0.67; // 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 *) { char **flist; int imx, err, ww, hh; float diffw, diffh; F1_help_topic = "HDF"; // help topic if (checkpend("all")) return; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } cimNF = 0; cim_manualwarp = 0; flist = gallery_getfiles(); // select images to combine if (! flist) goto cleanup; for (imx = 0; flist[imx]; imx++); // count selected files if (imx < 2 || imx > 9) { zmessageACK(Mwin,ZTX("Select 2 to 9 files")); goto cleanup; } cimNF = imx; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(flist[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[cimNF-1]); // curr_file = last in list if (err) goto cleanup; EFhdf.menufunc = m_HDF; EFhdf.funcname = "HDF"; 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 (HDFstat != 1) goto cancel; HDF_adjust(); // combine images based on user inputs if (HDFstat != 1) goto cancel; CEF->Fmods++; // done CEF->Fsaved = 0; edit_done(0); goto cleanup; cancel: edit_cancel(0); cleanup: if (flist) { for (imx = 0; flist[imx]; imx++) // free file list zfree(flist[imx]); zfree(flist); } 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; } // HDF 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 * HDF_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++) // bugfix 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 = HDFinitAlignSize / 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 = HDFinitSearchRange; // initial align search range cimSearchStep = HDFinitSearchStep; // initial align search step cimWarpRange = HDFinitWarpRange; // initial align corner warp range cimWarpStep = HDFinitWarpStep; // initial align corner warp step cimSampSize = HDFsampSize; // 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); // (y offset 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 = HDFimageIncrease; // 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 = HDFsearchRange; // align search range cimSearchStep = HDFsearchStep; // align search step size cimWarpRange = HDFwarpRange; // align corner warp range cimWarpStep = HDFwarpStep; // 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; } for (imx = 0; imx < cimNF; imx++) // use final warped images as basis { // for manual align adjustments PXM_free(cimPXMs[imx]); cimPXMs[imx] = PXM_copy(cimPXMw[imx]); } Fzoom = Fblowup = 0; Ffuncbusy = 0; HDFstat = 1; thread_exit(); return 0; // not executed } // paint and warp output image zdialog *HDFadjustzd = 0; // paint/warp dialog int HDFmode; // mode: paint or warp int HDFimage; // current image (0 based) int HDFradius; // paint mode radius char *HDFpixmap = 0; // map input image per output pixel float *HDFwarpx[10], *HDFwarpy[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); // image (o) 1 (o) 2 (o) 3 ... // (o) paint radius [___] // (o) warp [__] HDFadjustzd = zdialog_new(ZTX("Paint and Warp Image"),Mwin,Bdone,Bcancel,null); zdialog *zd = HDFadjustzd; zdialog_add_widget(zd,"hbox","hbim","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labim","hbim",ZTX("Image"),"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,"spin","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 HDFmode = 0; // start in paint mode HDFimage = 0; // initial image HDFradius = 100; // paint radius takeMouse(HDF_mousefunc,0); // connect mouse function cc = E3pxm->ww * E3pxm->hh; // allocate pixel map HDFpixmap = (char *) zmalloc(cc); memset(HDFpixmap,cimNF,cc); // initial state, blend all images for (imx = 0; imx < cimNF; imx++) { // allocate warp memory ww = cimPXMw[imx]->ww; hh = cimPXMw[imx]->hh; HDFwarpx[imx] = (float *) zmalloc(ww * hh * sizeof(float)); HDFwarpy[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,"-10/20"); // 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 (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (zd->zstat) // dialog finish { zdialog_destroy(zd); // prevent double [cancel] 15.02 freeMouse(); // disconnect mouse function signal_thread(); wrapup_thread(8); if (zd->zstat == 1) HDFstat = 1; else HDFstat = 0; if (HDFstat == 1) cim_trim(); // cut-off edges HDFmode = 0; zfree(HDFpixmap); // free pixel map for (imx = 0; imx < cimNF; imx++) { zfree(HDFwarpx[imx]); // free warp memory zfree(HDFwarpy[imx]); } } if (strmatch(event,"paint")) { // set paint mode zdialog_fetch(zd,"paint",nn); if (! nn) return 1; HDFmode = 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; HDFmode = 1; draw_mousecircle(0,0,0,1); // 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) HDFimage = nn - 1; // 0 to cimNF-1 signal_thread(); } if (strmatch(event,"radius")) // change paint radius zdialog_fetch(zd,"radius",HDFradius); if (strmatch(event,"focus")) { // toggle mouse capture takeMouse(HDF_mousefunc,0); // connect mouse function if (HDFmode == 1) gdk_window_set_cursor(gdkwin,dragcursor); // warp mode, drag cursor signal_thread(); } return 1; } // HDF dialog mouse function // paint: during drag, selected image >> HDFpixmap (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 (HDFmode == 0) goto paint; else if (HDFmode == 1) goto warp; else return; paint: radius = HDFradius; // paintbrush radius radius2 = radius * radius; draw_mousecircle(Mxposn,Myposn,radius,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 HDFpixmap[imx] = HDFimage; imx = HDFimage; 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); // update window draw_mousecircle(mx,my,radius,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 = HDFpixmap[imx]; // select corresp. image to warp if (imx == cimNF) return; // else no action if (imx != HDFimage) { HDFimage = imx; // update selected image and imageN[5] = '1' + imx; // dialog radio button zdialog_stuff(HDFadjustzd,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; HDFwarpx[imx][ii] += dispx; // add this drag to prior sum HDFwarpy[imx][ii] += dispy; dispx = HDFwarpx[imx][ii]; dispy = HDFwarpy[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 15.09 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 = HDFpixmap[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 = 5.0; // initial search range, +/- pixels float STP_initSearchStep = 1.0; // initial search step, pixels float STP_initWarpRange = 2.0; // initial corner warp range float STP_initWarpStep = 1.0; // initial corner warp step float STP_searchRange = 2.0; // normal search range float STP_searchStep = 1.0; // normal search step float STP_warpRange = 1.0; // normal corner warp range float STP_warpStep = 0.67; // normal corner warp step void * STP_align_thread(void *); void STP_adjust(); void STP_mousefunc(); void * STP_adjust_thread(void *); editfunc EFstp; // edit function data // menu function void m_STP(GtkWidget *, cchar *) { char **flist; int imx, err, ww, hh; float diffw, diffh; F1_help_topic = "stack_paint"; // help topic if (checkpend("all")) return; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } cimNF = 0; cim_manualwarp = 0; flist = gallery_getfiles(); // select images to combine if (! flist) goto cleanup; for (imx = 0; flist[imx]; imx++); // count selected files if (imx < 2 || imx > 9) { zmessageACK(Mwin,ZTX("Select 2 to 9 files")); goto cleanup; } cimNF = imx; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(flist[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[cimNF-1]); // curr_file = last in list if (err) goto cleanup; EFstp.menufunc = m_STP; EFstp.funcname = "stack-paint"; 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: if (flist) { for (imx = 0; flist[imx]; imx++) // free file list zfree(flist[imx]); zfree(flist); } 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 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 * STP_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 = 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 } 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; STP_stat = 1; thread_exit(); return 0; // not executed } // paint output image int STP_image; // current image (0 based) int STP_radius; // paint mode radius char *STP_pixmap = 0; // map input image per output pixel void STP_adjust() { zdialog *zd; char imageN[8] = "imageN", labN[4] = "0"; int cc, imx; int STP_adjust_dialog_event(zdialog *zd, cchar *event); // image (o) 1 (o) 2 (o) 3 ... // radius [___] 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",ZTX("Image"),"space=5"); zdialog_add_widget(zd,"hbox","hbmr","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labr","hbmr",Bradius,"space=5"); zdialog_add_widget(zd,"spin","radius","hbmr","1|400|1|100"); 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 takeMouse(STP_mousefunc,0); // connect mouse function cc = E3pxm->ww * E3pxm->hh; // allocate pixel map STP_pixmap = (char *) zmalloc(cc); memset(STP_pixmap,cimNF,cc); // initial state, blend all images start_thread(STP_adjust_thread,0); // start working thread signal_thread(); zdialog_resize(zd,200,0); zdialog_run(zd,STP_adjust_dialog_event,"-10/20"); // 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 (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (zd->zstat) // dialog finish { zdialog_destroy(zd); // prevent double [cancel] 15.02 freeMouse(); // disconnect mouse function signal_thread(); wrapup_thread(8); if (zd->zstat == 1) STP_stat = 1; else STP_stat = 0; if (STP_stat == 1) cim_trim(); // cut-off edges zfree(STP_pixmap); // free pixel map } 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 signal_thread(); } if (strmatch(event,"radius")) // change paint radius zdialog_fetch(zd,"radius",STP_radius); if (strmatch(event,"focus")) { // toggle mouse capture takeMouse(STP_mousefunc,0); // connect mouse function signal_thread(); } return 1; } // STP dialog mouse function // during drag, selected image >> STP_pixmap (within paint radius) >> E3 void STP_mousefunc() { float vpix1[4], *pix3; int imx, radius, rect, radius2, vstat1; int mx, my, dx, dy, px3, py3; float px1, py1; float xoff, yoff, sintf[10], costf[10]; radius = STP_radius; // paintbrush radius radius2 = radius * radius; rect = 2 * radius; // draw mouse selection circle draw_mousecircle(Mxposn,Myposn,radius,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 (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 STP_pixmap[imx] = STP_image; imx = STP_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 } Fpaint3(mx-radius,my-radius,rect,rect); // update window draw_mousecircle(mx,my,radius,0); // draw mouse selection circle return; } // Combine images in E3pxm (not reallocated). Update main window. void * STP_adjust_thread(void *) { void * STP_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(STP_combine_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fpaint2(); } return 0; // not executed } void * STP_combine_wthread(void *arg) // worker thread { int index = *((int *) (arg)); int px3, py3, vstat1, imx; float red, green, blue; // float 15.09 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 = STP_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/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 = 6000; // pixel sample size float STN_initSearchRange = 5.0; // initial search range, +/- pixels float STN_initSearchStep = 1.0; // initial search step, pixels float STN_initWarpRange = 2.0; // initial corner warp range float STN_initWarpStep = 1.0; // initial corner warp step float STN_searchRange = 2.0; // normal search range float STN_searchStep = 1.0; // normal search 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_STN(GtkWidget *, cchar *) { char **flist; int imx, err, ww, hh; float diffw, diffh; F1_help_topic = "stack_noise"; // help topic if (checkpend("all")) return; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } cimNF = 0; cim_manualwarp = 0; flist = gallery_getfiles(); // select images to combine if (! flist) goto cleanup; for (imx = 0; flist[imx]; imx++); // count selected files if (imx < 2 || imx > 9) { zmessageACK(Mwin,ZTX("Select 2 to 9 files")); goto cleanup; } cimNF = imx; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(flist[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[cimNF-1]); // curr_file = last in list if (err) goto cleanup; EFstn.menufunc = m_STN; 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: if (flist) { for (imx = 0; flist[imx]; imx++) // free file list zfree(flist[imx]); zfree(flist); } 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 lowest value // [x] omit highest value 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,"-10/20"); // 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 (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (zd->zstat) { // dialog finish zdialog_destroy(zd); // prevent double [cancel] 15.02 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_combine_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_combine_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fpaint2(); // update window } return 0; // not executed } // worker thread void * STN_combine_wthread(void *arg) { int index = *((int *) arg); int imx, vstat, px3, py3; float red, green, blue; // float 15.09 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]; // input layers 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}, {2,4}, {2,5}, {3,5}, {3,6} }; 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 blend width during auto-align float panFinalBlend = 0.05; // final blend width * ww float panBlendDecrease = 0.7; // blend width reduction per align cycle float panInitSearchRange = 6.0; // initial search range, +/- pixels float panInitSearchStep = 1.0; // initial search step, pixels float panInitWarpRange = 12.0; // initial corner warp range, +/- pixels float panInitWarpStep = 2.0; // initial corner warp step, pixels float panSearchRange = 3.0; // normal search range, +/- pixels float panSearchStep = 1.0; // normal search step, pixels float panWarpRange = 4.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 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(GtkWidget *, cchar *) { int imx, err; char **flist = 0; F1_help_topic = "panorama"; // help topic if (checkpend("all")) return; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } cimNF = 0; cim_manualwarp = 0; pano_mousewarp = 0; flist = gallery_getfiles(); // select images to combine if (! flist) goto cleanup; for (imx = 0; flist[imx]; imx++); // count selected files if (imx < 2 || imx > 4) { zmessageACK(Mwin,ZTX("Select 2 to 4 files")); goto cleanup; } cimNF = imx; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(flist[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[cimNF-1]); // curr_file = last in list if (err) goto cleanup; EFpano.menufunc = m_pano; EFpano.funcname = "pano"; 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: if (flist) { for (imx = 0; flist[imx]; imx++) // free file list zfree(flist[imx]); zfree(flist); } 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 | | [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,"spin","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","hb3","dialog"); zdialog_add_widget(panozd,"check","nocurve","hb3",scan_mess,"space=5"); zdialog_add_widget(panozd,"hbox","hb4","dialog"); zdialog_add_widget(panozd,"check","manwarp","hb4",ZTX("no auto warp"),"space=5"); zdialog_add_widget(panozd,"hbox","hb5","dialog"); 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"); 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"); 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,"-10/20"); // 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,"escape")) zd->zstat = 2; // escape = cancel 15.07 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,"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); zmainloop(); 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 } if (xf_range < xf_rfinal) goto done; // finished snprintf(paneltext,200,"align: %d match: %.5f lens: %.1f", cimNsearch, matchB, lens_mmB); zmainloop(); 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; } 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; 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; } 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 | | | | [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,"spin","red","hbc2","50|200|0.1|100","space=5"); zdialog_add_widget(panozd,"spin","green","hbc2","50|200|0.1|100","space=5"); zdialog_add_widget(panozd,"spin","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,"spin","bright","hbbri","50|200|0.1|100"); zdialog_add_widget(panozd,"button","rgbapp","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,"spin","blend","hbblen","1|500|1|1"); zdialog_add_widget(panozd,"button","apply","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,"button","flatten","hbflat",Bflatten,"space=3"); zdialog_add_widget(panozd,"label","labflat","hbflat",ZTX("curved image"),"space=5"); for (imx = 0; imx < cimNF; imx++) { // add radio button per image imageN[5] = '0' + imx; zdialog_add_widget(panozd,"radio",imageN,"hbim"); } zdialog_resize(panozd,300,0); 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,"-10/20"); // 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; int imS, imx, im1, im2; int ww, hh, px, py, nn; float bright, bright2, *pixel; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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]); 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,"rgbapp")) // apply color & brightness changes { 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 { 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 (! 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,"apply")) // apply new blend width { 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 zdialog_fetch(zd,"mousewarp",pano_mousewarp); if (pano_mousewarp) takeMouse(panowarp_mousefunc,dragcursor); // connect mouse function else freeMouse(); } if (strmatch(event,"flatten")) { cim_uncurve_image(); 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_vpano(GtkWidget *, cchar *) { int imx, err; char **flist = 0; F1_help_topic = "panorama"; // help topic if (checkpend("all")) return; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } cimNF = 0; cim_manualwarp = 0; pano_mousewarp = 0; flist = gallery_getfiles(); // select images to combine if (! flist) goto cleanup; for (imx = 0; flist[imx]; imx++); // count selected files if (imx < 2 || imx > 4) { zmessageACK(Mwin,ZTX("Select 2 to 4 files")); goto cleanup; } cimNF = imx; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(flist[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[cimNF-1]); // curr_file = last in list if (err) goto cleanup; EFvpano.menufunc = m_vpano; EFvpano.funcname = "vpano"; 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: if (flist) { for (imx = 0; flist[imx]; imx++) // free file list zfree(flist[imx]); zfree(flist); } 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 | | [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,"spin","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","hb3","dialog"); zdialog_add_widget(panozd,"check","nocurve","hb3",scan_mess,"space=5"); zdialog_add_widget(panozd,"hbox","hb4","dialog"); zdialog_add_widget(panozd,"check","manwarp","hb4",ZTX("no auto warp"),"space=5"); zdialog_add_widget(panozd,"hbox","hb5","dialog"); 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"); 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"); 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,"-10/20"); // 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,"escape")) zd->zstat = 2; // escape = cancel 15.07 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,"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.05); // 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); zmainloop(); 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 } if (xf_range < xf_rfinal) goto done; // finished snprintf(paneltext,200,"align: %d match: %.5f lens: %.1f", cimNsearch, matchB, lens_mmB); zmainloop(); 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; } 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; 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; } 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 | | | | Select image (o) (o) (o) (o) | | | | red green blue | | [_____-|+] [_____-|+] [_____-|+] | | brightness [___-|+] [apply] | | [auto color] [file color] | | blend width [___] [apply] | | [x] mouse warp | | | | [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("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,"spin","red","hbc2","50|200|0.1|100","space=5"); zdialog_add_widget(panozd,"spin","green","hbc2","50|200|0.1|100","space=5"); zdialog_add_widget(panozd,"spin","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,"spin","bright","hbbri","50|200|0.1|100"); zdialog_add_widget(panozd,"button","rgbapp","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,"spin","blend","hbblen","1|500|1|1"); zdialog_add_widget(panozd,"button","apply","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"); 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_resize(panozd,300,0); 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,"-10/20"); // 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; int imS, imx, im1, im2; int ww, hh, px, py, nn; float bright, bright2, *pixel; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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]); 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,"rgbapp")) // apply color & brightness changes { 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 { 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 (! 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,"apply")) // apply new blend width { 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 zdialog_fetch(zd,"mousewarp",pano_mousewarp); if (pano_mousewarp) takeMouse(panowarp_mousefunc,dragcursor); // connect mouse function else freeMouse(); } return 1; } /**************************************************************************/ // make a multi-row panorama via Panorama Tools package (Hugin version) void m_pano_PT(GtkWidget *, cchar *) // 15.02 { int im, nf, err; char **flist = 0, file1[200], *file2; char *olddir = 0, *pp; FILE *fid; struct stat statb; F1_help_topic = "panorama"; // help topic if (! PTtools) { zmessageACK(Mwin,ZTX("pano tools (hugin) not installed")); return; } flist = gallery_getfiles(); // get input images if (! flist) goto cleanup; for (nf = 0; flist[nf]; nf++); // count selected files if (nf < 2) { zmessageACK(Mwin,ZTX("Select at least 2 files")); goto cleanup; } if (checkpend("all")) return; Fblock = 1; // 15.11 olddir = get_current_dir_name(); // save curr. directory 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 < nf; im++) // for member image files fprintf(fid," \"%s\" ",flist[im]); fprintf(fid,"\n"); fprintf(fid,"%s\n","cpfind --celeste --multirow -o PTpano.pto PTpano.pto"); // find control points fprintf(fid,"%s\n","cpclean -o PTpano.pto PTpano.pto"); // remove flakey ones fprintf(fid,"%s\n","autooptimiser -a -m -l -s -o PTpano.pto PTpano.pto"); // optimize colors etc. fprintf(fid,"%s\n","pano_modify --crop=AUTO -o PTpano.pto PTpano.pto"); // cut off black margins fprintf(fid,"%s\n","pto2mk -o PTpano.mk -p PTpano PTpano.pto"); // convert to make file fprintf(fid,"%s\n","make -f PTpano.mk all"); // execute make file 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,200,"%s/PTpano.tif",tempdir); // look for output file err = stat(file1,&statb); if (err) goto cleanup; file2 = zstrdup(flist[0],12); // make fotoxx output file pp = strrchr(file2,'.'); // = ( 1st input file )-PT.tif if (! pp) pp = file2; strcpy(pp,"-PT.tif"); err = shell_ack("cp -f \"%s\" \"%s\" ",file1,file2); // copy /tmp/.../PTpano.tif if (err) goto cleanup; // to fotoxx output file Fblock = 0; // 15.11 f_open(file2,0,0,1,0); // and open it as curr. file cleanup: if (olddir) { err = chdir(olddir); // restore curr. directory free(olddir); } if (flist) { for (im = 0; flist[im]; im++) // free file list zfree(flist[im]); zfree(flist); } Fblock = 0; // 15.11 return; } fotoxx-15.11.1/fotoxx.h0000644000175000017500000026774112616075370013377 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. ***************************************************************************/ #include #include #include #include #include "zfuncs.h" // Fotoxx definitions #define Frelease "Fotoxx 15.11.1" // Fotoxx release version #define Flicense "Free software - GNU General Public License v.3" #define Fhomepage "http://kornelix.com/fotoxx" #define Fsoftware "Software used: GNU, GTK, libtiff, libpng, liblcms, DCraw, exiftool" #define Fcontact "Questions and bugs: kornelix@posteo.de" #define Ftranslators "Translators: \n" \ " Doriano Blengino, Arthur Kalverboer, André Campos Rodovalho, \n" \ " Peter Landgren, J. A. Miralles Puignau, Pierrick Pinot " #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 = zero to perfect color match #define RGBMATCH(r1,g1,b1,r2,g2,b2) \ (1.0 - 0.002762 * sqrtf((r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(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 wwhh_limit 20000 // max. image width/height 15.03 #define max_threads 4 // max. threads (hyperthreads useless) #define maxtopdirks 200 // max. no. of top image directories #define image_index_max 2000 // max. image files per image index file #define indexrecl 2000 // max. image index rec. (>XFCC >tagFcc) #define maximages 500000 // max. image files supported #define maxgeolocs 20000 // max. geolocation recs (cities) #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 10000 // max total tags and tags/category 15.07 #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 // EXIF/IPTC keys for embedded image metadata #define iptc_keywords_key "Keywords" // keywords (tags) (IPTC) #define iptc_rating_key "Rating" // star rating (IPTC) #define exif_size_key "ImageSize" // image size, NNNNxNNNN #define exif_date_key "DateTimeOriginal" // date/time (EXIF) #define exif_width_key "ImageWidth" // width, pixels (EXIF) #define exif_height_key "ImageHeight" // height, pixels (EXIF) #define exif_orientation_key "Orientation" // orientation (EXIF) #define exif_editlog_key "ImageHistory" // edit history log (EXIF) #define exif_comment_key "Comment" // image comment (EXIF) 15.10 #define exif_usercomment_key "UserComment" // image comment (EXIF) 15.10 #define iptc_caption_key "Caption-Abstract" // image caption (IPTC) #define exif_focal_length_key "FocalLengthIn35mmFormat" // focal length (EXIF) #define exif_city_key "City" // city name (geotags) #define exif_country_key "Country" // country name #define exif_latitude_key "GPSLatitude" // latitude in degrees (-180 to +180) #define exif_longitude_key "GPSLongitude" // longitude in degrees (-180 to +180) #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 int monitorDPI; // monitor dots/inch extern char zappname[20]; // app name/version extern char zlang[8]; // current language lc_RC extern char zicondir[200]; // where application icons live 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 int open_popup_windows; // open popup window count extern pthread_t tid_main; // main() thread ID } // externals from f.file module namespace navi { // gallery() data extern char *galleryname; // directory path or gallery type name extern int gallerytype; // 1-6 = dirk/search/metadata/album/recent/new extern int topfileposn; // gallery window target scroll position extern int nfiles; // gallery file count (incl. subdirks) extern int nimages; // gallery image file count extern char **mdlist; // corresp. metadata list extern int mdrows; // text rows in metadata list 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 int gallery_paint(GtkWidget *, cairo_t *); // gallery window paint function void menufuncx(GtkWidget *win, 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 int KBrelease(GtkWidget *, GdkEventKey *, void *); // gallery window key release event } // GTK etc. parameters EX int screenww, screenhh; // monitor screen size EX GtkWidget *Mwin, *Mvbox; // main window EX GtkWidget *Fhbox, *Fmenu, *Fvbox, *Fpanel, *Fpanlab, *Fdrawin; // F window widgets EX GtkWidget *Ghbox, *Gmenu, *Gvbox, *Gsep, *Gpanel, *Gtop, *Galbum; // G window widgets EX GtkWidget *Gscroll, *Gdrawin; EX GtkAdjustment *Gadjust; EX GtkWidget *Whbox, *Wmenu, *Wvbox, *Wdrawin; // W window widgets EX GtkWidget *Cdrawin; // curr. drawing window, Fdrawin/Wdrawin EX GdkWindow *gdkwin; // corresp. GDK window EX cairo_t *mwcr; // cairo context for drawing windows EX GdkRGBA bgcolor; // background color 15.09 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 // user options EX char *menu_style; // menu style: icons/text/both EX char *dialog_font; // zdialog font and size 15.09 EX int iconsize; // menu icon size EX int thumbfilesize; // thumbnail files, pixel size 15.02 EX char *startdisplay; // 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 zoomcount; // zoom count to reach 2x (1/2/3/4) EX float zoomratio; // corresp. zoom ratio: 2**(1/zoomcount) EX char *DCrawcommand; // command, RAW to tiff using DCraw EX char *RAWfiletypes; // known RAW file types: .raw .rw2 ... EX char *myRAWtypes; // RAW types encountered // general parameters EX char *Prelease; // prior fotoxx version EX int Ffirsttime; // first time startup 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 FGW; // curr. window mode: 'F' 'G' 'W' #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 200 EX menutab_t menutab[maxmenus]; EX int Nmenus; #define maxshortcuts 50 EX char *shortcutkey[50]; // KB shortcut keys (or combinations) EX char *shortcutmenu[50]; // corresponding menu names EX int Nshortcuts; // count of entries EX int Fshutdown; // app shutdown underway EX int Fdebug; // debug flag EX int Fnoindex; // disable image index at startup 15.02 EX int FindexOK; // index function completed OK 15.04 EX int Fpaintlock; // lock for painting main window EX int Fkillfunc; // flag, running function should quit EX int Fmetamod; // image metadata unsaved changes 15.03 EX int Fgtagmod; // geotags metadata unsaved changes EX int Fblock; // edit functions mutex flag 15.04 EX int Ffuncbusy; // function is busy/working EX int Fthreadbusy; // thread is busy/working EX int Fdcraw; // flag, DCraw available EX int Fufraw; // flag, UFraw available EX int Frawtherapee; // flag, Raw Therapee available EX int Fbrasero; // flag, Brasero available EX int PTtools; // flag, pano tools available EX int Ffullscreen; // flag, window is fullscreen EX int Fpaintrequest; // window paint request pending 15.05 EX int Fimagerefresh; // window image refresh needed 15.05 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 ss_escape; // KB escape or F11 >> slide show process EX int ss_Larrow; // KB left arrow >> slide show process EX int ss_Rarrow; // KB right arrow >> slide show process EX int ss_spacebar; // KB space bar >> slide show process EX int ss_Bkey; // KB B-key >> slide show process EX int ss_Mkey; // KB M-key >> slide show process EX char *cycledesktop_album; // cycle desktop wallpaper, album EX int cycledesktop_index; // cycle desktop wallpaper, file index 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 *startmenu; // command line, startup menu function 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 Fwarnalpha; // warn if save image with alpha to jpeg EX int Fscriptbuild; // edit script build is in-progress 15.10 volatile // stop optimizer removing code EX double Fbusy_goal, Fbusy_done; // BUSY message progress tracking EX char paneltext[200]; // top panel application text EX char tempdir[200]; // directory for temp files EX char *topdirks[maxtopdirks]; // user top-level image directories EX int Ntopdirks; // topdirk[] count EX char *thumbdirk; // thumbnails directory EX char *curr_file, *curr_dirk; // current image file and directory EX char *initial_file; // initial file on command line 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 15.05 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 **filecache; // cache for moving image files around EX int Nfilecache; // count of image files in cache EX char *last_curr_file; // curr_file in last session EX char *last_galleryname; // galleryname in last session EX int last_gallerytype; // gallerytype in last session EX char *colormapfile; // curr. printer color map file EX char f_load_type[8]; // data set by f_load() 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, f_save_size; // 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 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 writetext_dirk[200]; // directory for write_text 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 pattern_dirk[200]; // directory for pattern files EX char printer_color_dirk[200]; // directory for printer calibration 15.10 EX char edit_scripts_dirk[200]; // directory for edit script files 15.10 EX char maps_dirk[200]; // directory for map files EX char searchresults_file[200]; // file for search results // 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 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 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 t_spcfunc(int spc); // callback function, spline curve edit EX float splcurve_minx; // min. anchor point dist, % scale 15.10 typedef struct { // spline curve data GtkWidget *drawarea; // drawing area for spline curves t_spcfunc *spcfunc; // callback function when curve changed int Nspc; // number of curves, 1-10 int fact[10]; // curve is active 15.10 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 */ volatile // 15.01 EX 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_firewall; // firewall: no search thru selected pixels 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_finOK; // finish area - success EX int sa_finhole; // finish area - area has a hole EX int sa_fincancel; // finish area - user cancel 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 int sa_Npixel; // total select area pixel count 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 metadata items used by fotoxx (EXIF/IPTC, image index file) EX char meta_pdate[16]; // image (photo) date, yyyymmddhhmmss EX char meta_ppdate[16]; // previous image date read or set EX char meta_rating[4]; // image rating in stars, "0" to "5" EX char meta_size[16]; // image size, "NNNNxNNNN" EX char meta_tags[tagFcc]; // tags for current image file EX char meta_comments[exif_maxcc]; // image comments expanded EX char meta_caption[exif_maxcc]; // image caption EX char meta_city[100], meta_country[100]; // geolocs: city, country EX char meta_latitude[20], meta_longitude[20]; // geolocs: lati/longitude (-123.4567) // image index file record. // tags, comms, capt, gtags may have "null" (char) as a value. struct sxrec_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[4]; // IPTC rating, '0' to '5' stars char *tags; // IPTC tags char *capt; // IPTC caption char *comms; // EXIF comments char *gtags; // EXIF geotags }; EX FILE *sxrec_fid; // open file ID - get_sxrec() // 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 15.10 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[200]; // 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 15.04 }; 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 15.04 }; // 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 *zdbatchtags; // batch tags zdialog EX zdialog *zdexifview; // view EXIF/IPTC zdialog EX zdialog *zdexifedit; // edit EXIF/IPTC zdialog EX zdialog *zdeditgeotags; // add geotags zdialog EX zdialog *zdfilesave; // file save zdialog EX zdialog *zdrename; // rename file zdialog EX zdialog *zdcopymove; // copy/move file zdialog EX zdialog *zddelete; // delete file zdialog EX zdialog *zdtrash; // trash file zdialog EX zdialog *zdsela; // select area zdialog EX zdialog *zdmagnify; // magnify image zdialog EX zdialog *zd_gallery_getfiles; // gallery_getfiles 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 trimrect[4]; // trim rectangle, x1, y1, x2, y2 EX int trimsize[2]; // trim (crop) width, 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 int gtimefunc(void *arg); // periodic function void update_Fpanel(); // update F window information panel 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); // update Mpxb area from updated E3 area void Fpaint4(int px, int py, int ww, int hh); // update Dpxb area from updated Mpxb void mouse_event(GtkWidget *, GdkEventButton *, void *); // mouse event function void m_zoom(GtkWidget *, cchar *); // zoom image +/- void KBstate(GdkEventKey *event, int state); // 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 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); // draw pixel void erase_pixel(int px, int py); // erase pixel void draw_line(int x1, int y1, int x2, int y2, int type); // draw line or dotted line (type 1/2) void erase_line(int x1, int y1, int x2, int y2); // erase line void add_topline(int x1, int y1, int x2, int y2, int type); // add to list of pre-set overlay lines void draw_toplines(int arg); // draw all pre-set overlay lines void draw_gridlines(); // 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(); // 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); // draw text on window void add_topcircle(int px, int py, int radius); // draw circle on window void draw_topcircles(); // draw circles when window repainted void erase_topcircles(); // remove all circles void draw_mousecircle(int cx, int cy, int cr, int Ferase); // draw circle around mouse pointer 15.08 void draw_mousecircle2(int cx, int cy, int cr, int Ferase); // 2nd circle for paint/clone void draw_mousearc(int cx, int cy, int ww, int hh, int Ferase); // 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 15.07 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 15.10 int edit_save_widgets(editfunc *EF, FILE *fid = 0); // save zdialog widgets to a file 15.10 int edit_save_last_widgets(editfunc *EF); // save last-used zdialog widgets 15.10 int edit_load_prev_widgets(editfunc *EF); // load last-used zdialog widgets 15.10 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 and menus for F/G/W view modes void m_viewmode(GtkWidget *, cchar *fgw); // set current F/G/W view mode void m_favorites(GtkWidget *, cchar *); // graphic popup menu, user favorites 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 // 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_previous(GtkWidget *, cchar *); // open previously accessed file void m_ufraw(GtkWidget *, cchar *); // open, edit camera RAW file with ufraw void m_rawtherapee(GtkWidget *, cchar *); // open, edit camera RAW file with Raw Therapee 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_create(GtkWidget *, cchar *); // create new blank image file, dialog int create_blank_file(cchar *file, int ww, int hh, int RGB[3]); // create new blank image file, callable void m_rename(GtkWidget *, cchar *); // rename an image file (same location) void m_copytoloc(GtkWidget *, cchar *); // copy an image file to a new location void m_movetoloc(GtkWidget *, cchar *); // move an image file to a new location void copy_move(cchar *menu); // copy/move shared function void m_copyto_clip(GtkWidget *, cchar *file); // copy an image file to the clipboard int file_copytoclipboard(char *file); // copy image file to GTK clipboard void m_trash(GtkWidget *, cchar *); // move an image file to trash void m_delete(GtkWidget *, cchar *); // delete an image file (undo not poss.) 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 didk char * file_new_version(char *file); // get next avail. file version name 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) 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_help(GtkWidget *, cchar *menu); // various help menu functions int find_imagefiles(cchar *dir, char **&f, int &n, int t, int z = 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 // metadata menu functions (f.metadata.cc) 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 meta_view(int length); // view EXIF/IPTC data in popup window void m_captions(GtkWidget *, cchar *); // show caption/comments at the top void m_edit_metadata(GtkWidget *, cchar *); // edit date/rating/tags dialog void metadate_pdate(cchar *metadate, char *pdate, char *ptime); // convert yyyymmddhhmmss to readable format void manage_tags(); // manage tags dialog function 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_batch_tags(GtkWidget *, cchar *); // add/remove tags for selected files void m_batch_renametags(GtkWidget *, cchar *); // rename tags for all image files void m_batch_metadata(GtkWidget *, cchar *); // add/change/delete metadata, selected files void load_filemeta(cchar *file); // EXIF/IPTC >> tags_* in memory void save_filemeta(cchar *file); // tags_* in memory >> EXIF/IPTC void update_image_index(cchar *file); // update image index file void delete_image_index(cchar *file); // delete entry from image index file void m_load_geomap(GtkWidget *, cchar *); // load a geomap chosen by user void geomap_paint_dots(); // paint red dots on map where image geotags void geomap_mousefunc(); // mouse function for geomap view mode void free_geomap(); // free memory used by geomap image void m_mapsearch_range(GtkWidget *, cchar *); // set search range from a geomap position void m_edit_geotags(GtkWidget *, cchar *); // edit geotags in image file EXIF void m_batch_geotags(GtkWidget *, cchar *menu); // batch add geotags void m_geotag_groups(GtkWidget *, cchar *); // report images grouped by date, geotags void m_search_images(GtkWidget *, cchar *); // search image metadata 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 int get_sxrec(sxrec_t &sxrec, cchar *file); // get image index rec. int get_sxrec_min(cchar *file, char *fdate, char *pdate, char *size); // get image index rec. for gallery paint int put_sxrec(sxrec_t *sxrec, cchar *file); // add/update image index rec. int read_sxrec_seq(sxrec_t &sxrec, int &ftf); // read image index recs. 1-last int write_sxrec_seq(sxrec_t *sxrec, int &ftf); // write image index recs. 1-last int image_fcomp(cchar *file1, cchar *file2); // file name compare in image index sequence // select area menu functions (f.area.cc) void m_select(GtkWidget *, cchar *); // select area within image 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_copy(GtkWidget *, cchar *); // copy an area for later paste 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 m_select_save(GtkWidget *, cchar *); // save area to file with alpha channel 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); // draw a connected line void sa_draw1pix(int px, int py); // add one pixel to select area void sa_redraw(); // redraw area edge void sa_mouse_select(); // select by mouse (opt. color match) void sa_nextseq(); // start next sequence number void sa_unselect_pixels(); // 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); // show or hide area outline void sa_show_rect(int px1, int py1, int ww, int hh); // show area outline inside a rectangle int sa_validate(); // validate area for curr. image context 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 +/- // image edit functions - Edit menu (f.edit.cc) void m_trimrotate(GtkWidget *, cchar *); // combined trim/rotate function void m_upright(GtkWidget *, cchar *); // upright rotated image void m_voodoo1(GtkWidget *, cchar *); // automatic image retouch void m_voodoo2(GtkWidget *, cchar *); // automatic image retouch void m_combo(GtkWidget *, cchar *); // combo function, brightness/contrast/color void m_britedist(GtkWidget *, cchar *); // edit brightness distribution void m_zonal_flatten(GtkWidget *, cchar *); // flatten zonal brightness distribution void m_tonemap(GtkWidget *, cchar *); // tone mapping void m_resize(GtkWidget *, cchar *); // resize image void m_flip(GtkWidget *, cchar *); // flip horizontally or vertically void m_write_text(GtkWidget *, cchar *); // write text on 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_write_line(GtkWidget *, cchar *); // write line/arrow on image 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_smart_erase(GtkWidget *, const char *); // smart erase object void m_redeye(GtkWidget *, cchar *); // red-eye removal void m_paint_clone(GtkWidget *, cchar *); // paint pixels with the mouse void m_paint_transp(GtkWidget *, cchar *); // paint transparnecy with the mouse void m_color_mode(GtkWidget *, cchar *); // B+W/color, negative/positive, sepia void m_shift_colors(GtkWidget *, cchar *); // shift one color into another color void m_adjust_RGB(GtkWidget *, cchar *); // color adjust using RGB or CMY colors void m_adjust_HSL(GtkWidget *, cchar *); // color adjust with HSL void m_brightramp(GtkWidget *, cchar *); // ramp brightness across image void m_color_ramp(GtkWidget *, cchar *); // RGB color ramp in selected image areas void m_match_color(GtkWidget *, cchar *); // set image2 colors to match image1 void m_color_profile(GtkWidget *, cchar *); // convert to another color profile void m_remove_dust(GtkWidget *, const char *); // remove dust void m_anti_alias(GtkWidget *, const char *); // anti-alias void m_color_fringes(GtkWidget *, const char *); // reduce chromatic abberation void m_stuck_pixels(GtkWidget *, const char *); // fix stuck pixels // image edit functions - Bend/Warp menu (f.bend.cc) void m_unbend(GtkWidget *, cchar *); // fix perspective problems void m_perspective(GtkWidget *, cchar *); // warp tetragon into rectangle void m_warp_area(GtkWidget *, cchar *); // warp image within an 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 // image edit functions - Effects menu (f.effects.cc) void m_colordep(GtkWidget *, cchar *); // set color depth 1-16 bits/color void m_sketch(GtkWidget *, cchar *); // convert image to pencil sketch void m_linedraw(GtkWidget *, cchar *); // comvert image to outline drawing void m_colordraw(GtkWidget *, cchar *); // convert image to solid color drawing void m_gradblur(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 dot array (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_waves(GtkWidget *, cchar *); // warp image using a wave pattern void m_dirblur(GtkWidget *, cchar *); // blur image in the direction of mouse drags void m_sphere(GtkWidget *, cchar *); // image spherical projection // 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_STP(GtkWidget *, cchar *); // stack / paint image void m_STN(GtkWidget *, cchar *); // stack / noise reduction void m_pano(GtkWidget *, cchar *); // make panorama composite image void m_vpano(GtkWidget *, cchar *); // make vertical panorama void m_pano_PT(GtkWidget *, cchar *); // make panorama via pano tools // mashup function - f.mashup.cc void m_mashup(GtkWidget *, cchar *); // arrange images on a background image // tools menu functions (f.tools.cc) void m_index(GtkWidget *, cchar *); // rebuild image index and thumbnails void index_rebuild(int menu); // index rebuild function void m_options(GtkWidget *, cchar *); // user options 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 stripe 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_darkbrite(GtkWidget *, cchar *); // highlight the darkest and brightest pixels void darkbrite_paint(); // paint function called from Fpaint() void m_moncolor(GtkWidget *, cchar *); // check monitor brightness and color void m_mongamma(GtkWidget *, cchar *); // adjust monitor gamma void m_changelang(GtkWidget *, cchar *); // change language void m_untranslated(GtkWidget *, cchar *); // report missing translations void m_calibrate_printer(GtkWidget *, cchar *); // calibrate printer colors void print_calibrated(); // print image with adjusted colors 15.10 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 = 0); // display image gallery window, navigate void set_gwin_title(); // main window title = gallery name char * gallery_getnext(int index, int lastver); // get prev/next file with last version option int gallery_position(cchar *file, int Nth); // get rel. position of file in gallery int image_file_type(cchar *file); // get file type 1-5: 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 char * image_thumbfile(char *imagefile, int *ind = 0); // get thumbnail filespec, create if missing PIXBUF * image_thumbnail(char *imagefile, int size = 0); // get thumbnail pixbuf, create if nec. PIXBUF * get_thumbnail_pixbuf(char *imagefile, int size); // make thumbnail pixbuf from image file void popimage(int Fnewin); // popup big image of clicked thumbnail void gallery_monitor(cchar *action); // start/stop gallery directory monitor char ** gallery_getfiles(char **initfiles = 0); // select files from gallery window void gallery_getfiles_Lclick_func(int Nth); // get files, thumbnail left-click void gallery_getfiles_Rclick_func(int Nth); // get files, 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 // albums menu (f.albums.cc) void m_manage_albums(GtkWidget *, cchar *); // create and edit image albums void album_drag_start(int Nth); // rearrange - get image to move void album_drag_drop(int Nth); // rearrange - put image here void album_drag_kill(); // abort drag/drop operation void album_remove(GtkWidget *, cchar *); // remove image from album void album_cutfile(GtkWidget *, cchar *); // remove image from album, add to cache void album_pastecache(GtkWidget *, cchar *); // paste image cache to album position void album_pastecurrfile(GtkWidget *, cchar *); // paste current file to album position 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 int file_addtocache(char *file); // add image file to file cache char * file_removefromcache(); // get image file from cache void file_clearcache(); // clear image file cache void m_slideshow(GtkWidget *, cchar *); // enter or leave slideshow mode void ss_imageprefs_Lclick_func(int Nth); // slideshow image prefs thumbnail click func void slideshow_next(cchar *); // show prev/next image void m_setdesktop(GtkWidget *, cchar *); // set desktop wallpaper from curr. file void m_cycledesktop(GtkWidget *, cchar *); // cycle desktop wallpaper from a Fotoxx album void run_cycledesktop(cchar *album, cchar *secs); // cycle desktop wallpaper function // batch menu functions (f.batch.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_dcraw(GtkWidget *, cchar *); // convert RAW files to tiff using DCraw void m_batch_rawtherapee(GtkWidget *, cchar *); // convert RAW files to tiff using Raw Therapee void m_scriptfiles(GtkWidget *, cchar *); // build and run edit script files 15.10 int addscript(editfunc *); // add edit function to script 15.10 void m_burn(GtkWidget *, cchar *); // burn selected images to CD/DVD void m_duplicates(GtkWidget *, cchar *); // find duplicate image files // PXM and PXB pixmap image functions (f.image.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); // 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); // 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 15.08 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 15.04 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() // 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 Bcolor ZTX("Color") #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 Bfileselected ZTX("%d files selected") #define Bfind ZTX("Find") #define Bfinish ZTX("Finish") #define Bflatten ZTX("Flatten") #define Bfont ZTX("Font") #define Bgeotags ZTX("Geotags") #define Bgreen ZTX("Green") #define Bgrid ZTX("Grid") #define Bheight ZTX("Height") #define Bhelp ZTX("Help") #define Bhide ZTX("Hide") #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 Bmax ZTX("Max") #define Bnegative ZTX("Negative") #define Bnew ZTX("New") #define Bnext ZTX("Next") #define Bno ZTX("No") #define Bnoimages ZTX("no images") #define Bnofileselected ZTX("no files selected") #define Bnoselection ZTX("no selection") #define BOK ZTX("OK") #define Bopen ZTX("Open") #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 Bsave ZTX("Save") #define Bsavetoedit ZTX("Unknown file type, save as tiff/jpeg/png to edit") #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 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-15.11.1/f.widgets.cc0000664000175000017500000015654312616075370014077 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** Fotoxx window and menu build functions build_widgets build widgets and menus for F/G/W view modes m_viewmode set current F/G/W 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, *mBatch, *mAlbums, *mMeta, *mArea; GtkWidget *mEdit, *mRep, *mBend, *mEff, *mComb, *mHelp; GtkWidget *popmenu_main, *popmenu_thumb, *popmenu_album, *popmenu_raw; int NFmenu, NGmenu, NWmenu; // initialize widgets and menus for F/G/W 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,Frelease); Mvbox = gtk_box_new(VERTICAL,0); // top container gtk_container_add(GTK_CONTAINER(Mwin),Mvbox); gtk_widget_show_all(Mwin); 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); drag_drop_connect(Mwin,m_open_drag); // connect drag-drop to main window // get hardware and set default font for screen widgets get_hardware_info(Mwin); // GTK API broken again 15.09 g_object_set(zfuncs::settings,"gtk-font-name",dialog_font,null); // set font for screen 15.09 // F view widgets - image file Fhbox = gtk_box_new(HORIZONTAL,0); // top container gtk_box_pack_start(GTK_BOX(Mvbox),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); // 15.04 Fpanlab = gtk_label_new("panel"); gtk_box_pack_start(GTK_BOX(Fpanel),Fpanlab,0,0,0); 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); // G view widgets - thumbnail gallery Ghbox = gtk_box_new(HORIZONTAL,0); // top container gtk_box_pack_start(GTK_BOX(Mvbox),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 15.08 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_separator_new(VERTICAL); // separator line gtk_box_pack_start(GTK_BOX(Gpanel),Gsep,0,0,3); 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,0); // W view widgets - geomaps Whbox = gtk_box_new(HORIZONTAL,0); // top container gtk_box_pack_start(GTK_BOX(Mvbox),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 geomap gtk_box_pack_start(GTK_BOX(Whbox),Wvbox,1,1,0); Wdrawin = gtk_drawing_area_new(); // geomap 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 geomap 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); // menu popup text (tool tips) ---------------------------------------- // F/G/W 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)"); // F view menu buttons cchar * favorites_tip = ZTX("Favorite Functions"); cchar * file_tip = ZTX("File: Open, RAW, Rename, Trash, 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 * bend_tip = ZTX("Bend: Fix Perspective, Bend/Warp image ..."); cchar * effects_tip = ZTX("Effects: Special Effects, Arty Transforms"); cchar * combine_tip = ZTX("Combine: HDR, HDF, Panorama, Stack, Mashup"); cchar * undo_redo_tip = ZTX("Undo or Redo one edit (left/right mouse click) \n" " hold key A to include all edits"); cchar * tools_tip = ZTX("Tools: Index, Options, Shortcuts, Magnify ..."); cchar * help_tip = ZTX("Help: Quick Start, User Guide, Recent Changes ..."); // G view menu buttons cchar * sync_tip = ZTX("Set gallery from current image file"); cchar * albums_tip = ZTX("Albums: Manage Albums, Slide Show, Desktop Wallpaper"); cchar * bookmarks_tip = ZTX("go to bookmarked image"); 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 * slow_scroll_tip = ZTX("slow scroll"); cchar * batch_tip = ZTX("Batch conversion, metadata, RAW processing"); // W view menu buttons cchar * load_geomap_tip = ZTX("Choose a map for image locations"); cchar * mapsearch_tip = ZTX("Set image search radius for map click"); // file popup menu cchar * clone_tip = ZTX("Open another window"); cchar * open_tip = ZTX("Open a new image file"); cchar * prevfiles_tip = ZTX("Open the previously seen file"); cchar * recentfiles_tip = ZTX("Open a recently seen file"); cchar * newfiles_tip = ZTX("Open a newly added file"); cchar * ufraw_tip = ZTX("Open and edit a camera RAW file"); cchar * rawtherapee_tip = ZTX("Open and edit a camera RAW file"); cchar * rename_tip = ZTX("Change the image file name"); cchar * create_tip = ZTX("Create a blank image"); cchar * trash_tip = ZTX("Move image file to Trash"); cchar * delete_tip = ZTX("Permanently delete 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 popup menu cchar * meta_view_short_tip = ZTX("List a few key metadata items"); cchar * meta_view_long_tip = ZTX("List all metadata items"); cchar * captions_tip = ZTX("(Toggle) show captions and comments"); cchar * edit_metadata_tip = ZTX("Edit image tags/caption/rating ..."); cchar * meta_edit_any_tip = ZTX("Edit any image metadata"); cchar * meta_delete_tip = ZTX("Remove all metadata from an image"); cchar * batch_tags_tip = ZTX("Add/remove tags for multiple images"); cchar * batch_renametags_tip = ZTX("Convert tag names for all images"); cchar * batch_metadata_tip = ZTX("Add/change/delete metadata for multiple images"); cchar * edit_geotags_tip = ZTX("Edit image location and geotags"); cchar * batch_geotags_tip = ZTX("Add/revise geotags for multiple images"); cchar * geotag_groups_tip = ZTX("Find all images for a location [date]"); cchar * search_images_tip = ZTX("Find images meeting select criteria"); // select area popup menu cchar * select_tip = ZTX("Select object or area for editing"); 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_paste_tip = ZTX("Paste previously copied area into image"); cchar * select_open_tip = ZTX("Open a file and paste as area into image"); cchar * select_save_tip = ZTX("Save area to a file with transparency"); // edit popup menu cchar * trimrotate_tip = ZTX("Trim/Crop margins and/or Rotate"); cchar * upright_tip = ZTX("Upright a rotated image"); cchar * voodoo1_tip = ZTX("Fast auto enhance that may work OK"); cchar * voodoo2_tip = ZTX("Fast auto enhance that may work OK"); cchar * combo_tip = ZTX("Adjust brightness, contrast, color"); cchar * tonemap_tip = ZTX("Add local contrast, enhance details"); cchar * britedist_tip = ZTX("Adjust brightness distribution"); cchar * zonal_flatten_tip = ZTX("Flatten zonal brightness distribution"); cchar * resize_tip = ZTX("Change pixel dimensions"); cchar * flip_tip = ZTX("Mirror image horizontally or vertically"); cchar * write_text_tip = ZTX("Write text on image"); cchar * write_line_tip = ZTX("Write lines or arrows on image"); cchar * brightramp_tip = ZTX("Fix brightness uniformity across 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 popup 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 * smart_erase_tip = ZTX("Remove unwanted objects"); cchar * redeye_tip = ZTX("Fix red-eyes from electronic flash"); cchar * paint_clone_tip = ZTX("Paint image pixels using the mouse"); cchar * paint_transp_tip = ZTX("Paint image transparency using the mouse"); cchar * color_mode_tip = ZTX("Make BW/color, negative/positive, sepia"); cchar * shift_colors_tip = ZTX("Shift/convert colors into other colors"); cchar * adjust_RGB_tip = ZTX("Adjust color using RGB or CMY colors"); cchar * adjust_HSL_tip = ZTX("Adjust color using HSL colors"); cchar * color_ramp_tip = ZTX("Adjust color in selected image areas"); cchar * match_color_tip = ZTX("Match colors on one image with another"); cchar * color_profile_tip = ZTX("Convert to another color profile"); cchar * remove_dust_tip = ZTX("Remove dust spots from scanned slides"); cchar * anti_alias_tip = ZTX("Smoothen edges with jaggies"); cchar * color_fringes_tip = ZTX("Reduce Chromatic Abberation"); cchar * stuck_pixels_tip = ZTX("Erase known hot and dark pixels"); // bend popup 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 * 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"); // effects popup menu cchar * colordep_tip = ZTX("Reduce color depth (posterize)"); cchar * sketch_tip = ZTX("Convert to pencil sketch"); cchar * linedraw_tip = ZTX("Convert to line drawing (edge detection)"); cchar * colordraw_tip = ZTX("Convert to solid color drawing"); cchar * gradblur_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 * waves_tip = ZTX("Warp an image with a wave pattern"); cchar * dirblur_tip = ZTX("Blur an image in the direction of mouse drags"); cchar * sphere_tip = ZTX("Make a spherical projection of an image"); // combine popup 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 * STP_tip = ZTX("Combine images to erase passing people, etc."); cchar * STN_tip = ZTX("Combine noisy images into a low-noise image"); cchar * pano_tip = ZTX("Combine images into a panorama"); cchar * vpano_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)"); // tools popup menu cchar * index_tip = ZTX("Index new files and make thumbnails"); cchar * options_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 * darkbrite_tip = ZTX("Highlight darkest and brightest pixels"); cchar * moncolor_tip = ZTX("Chart to adjust monitor color"); cchar * mongamma_tip = ZTX("Chart to adjust monitor gamma"); cchar * changelang_tip = ZTX("Change the GUI language"); cchar * untranslated_tip = ZTX("Report missing translations"); cchar * calibrate_printer_tip = ZTX("Calibrate printer colors"); cchar * resources_tip = ZTX("Memory and CPU (to terminal/logfile)"); // cchar * zappcrash_tip = "deliberate crash with traceback dump"; // batch popup 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_dcraw_tip = ZTX("Convert camera RAW files using DCraw"); cchar * batch_rawtherapee_tip = ZTX("Convert camera RAW files using Raw Therapee"); cchar * scriptfiles_tip = ZTX("Build and run edit script files"); cchar * burn_tip = ZTX("Burn selected image files to CD or DVD"); cchar * duplicates_tip = ZTX("Search all image files and report duplicates"); // metadata batch functions included here // help popup 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 * edit_funcs_summary_tip = ZTX("Summary of image edit functions"); 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"); // albums popup menu cchar * manage_albums_tip = ZTX("Organize images into albums"); cchar * slideshow_tip = ZTX("Start a slide show"); cchar * setdesktop_tip = ZTX("Set desktop wallpaper from current Fotoxx image"); cchar * cycledesktop_tip = ZTX("Cycle desktop wallpaper from a Fotoxx album"); // build menu entries ------------------------------------------------- #define MENUENT(_topmenu, _text, _icon, _desc, _func, _arg) \ { me = Nmenus++; \ 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 popup menus first mFile = create_popmenu(); MENUENT(mFile, ZTX("New Window"), 0, clone_tip, m_clone, 0 ); MENUENT(mFile, ZTX("Sync Gallery"), 0, sync_tip, navi::menufuncx, 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("Open Previous File"), 0, prevfiles_tip, m_previous, 0 ); MENUENT(mFile, ZTX("Open RAW (ufraw)"), 0, ufraw_tip, m_ufraw, 0 ); MENUENT(mFile, ZTX("Open RAW (Raw Therapee)"), 0, rawtherapee_tip, m_rawtherapee, 0 ); MENUENT(mFile, ZTX("New Blank Image"), 0, create_tip, m_create, 0 ); MENUENT(mFile, ZTX("Rename Image File"), 0, rename_tip, m_rename, 0 ); MENUENT(mFile, ZTX("Trash Image File"), 0, trash_tip, m_trash, 0 ); MENUENT(mFile, ZTX("Delete Image File"), 0, delete_tip, m_delete, 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("Set Desktop Wallpaper"), 0, setdesktop_tip, m_setdesktop, 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("Show Captions on Image"), 0, captions_tip, m_captions, 0 ); MENUENT(mMeta, ZTX("Edit Metadata"), 0, edit_metadata_tip, m_edit_metadata, 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("Batch Add/Remove Tags"), 0, batch_tags_tip, m_batch_tags, 0 ); MENUENT(mMeta, ZTX("Batch Rename Tags"), 0, batch_renametags_tip, m_batch_renametags, 0 ); MENUENT(mMeta, ZTX("Batch Add/Change Metadata"), 0, batch_metadata_tip, m_batch_metadata, 0 ); MENUENT(mMeta, ZTX("Edit Geotags"), 0, edit_geotags_tip, m_edit_geotags, 0 ); MENUENT(mMeta, ZTX("Batch Add Geotags"), 0, batch_geotags_tip, m_batch_geotags, 0 ); MENUENT(mMeta, ZTX("Images by Geotag"), 0, geotag_groups_tip, m_geotag_groups, 0 ); MENUENT(mMeta, ZTX("Search Images"), 0, search_images_tip, m_search_images, 0 ); mArea = create_popmenu(); MENUENT(mArea, ZTX("Select"), 0, select_tip, m_select, 0 ); MENUENT(mArea, ZTX("Show"), 0, select_show_tip, m_select_show, 0 ); MENUENT(mArea, ZTX("Hide"), 0, select_hide_tip, m_select_hide, 0 ); MENUENT(mArea, ZTX("Enable"), 0, select_enable_tip, m_select_enable, 0 ); MENUENT(mArea, ZTX("Disable"), 0, select_disable_tip, m_select_disable, 0 ); MENUENT(mArea, ZTX("Invert"), 0, select_invert_tip, m_select_invert, 0 ); MENUENT(mArea, ZTX("Unselect"), 0, select_unselect_tip, m_select_unselect, 0 ); MENUENT(mArea, ZTX("Copy Area"), 0, select_copy_tip, m_select_copy, 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, trimrotate_tip, m_trimrotate, 0 ); MENUENT(mEdit, ZTX("Upright"), 0, upright_tip, m_upright, 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, combo_tip, m_combo, 0 ); MENUENT(mEdit, ZTX("Brightness Dist."), 0, britedist_tip, m_britedist, 0 ); MENUENT(mEdit, ZTX("Zonal Flatten"), 0, zonal_flatten_tip, m_zonal_flatten, 0 ); MENUENT(mEdit, ZTX("Tone Mapping"), 0, tonemap_tip, m_tonemap, 0 ); MENUENT(mEdit, ZTX("Resize"), 0, resize_tip, m_resize, 0 ); MENUENT(mEdit, ZTX("Flip"), 0, flip_tip, m_flip, 0 ); MENUENT(mEdit, ZTX("Add Text"), 0, write_text_tip, m_write_text, 0 ); MENUENT(mEdit, ZTX("Add Lines"), 0, write_line_tip, m_write_line, 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("Smart Erase"), 0, smart_erase_tip, m_smart_erase, 0 ); MENUENT(mRep, ZTX("Red Eyes"), 0, redeye_tip, m_redeye, 0 ); MENUENT(mRep, ZTX("Paint/Clone"), 0, paint_clone_tip, m_paint_clone, 0 ); MENUENT(mRep, ZTX("Paint Transparency"), 0, paint_transp_tip, m_paint_transp, 0 ); MENUENT(mRep, ZTX("Color Mode"), 0, color_mode_tip, m_color_mode, 0 ); MENUENT(mRep, ZTX("Shift Colors"), 0, shift_colors_tip, m_shift_colors, 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("Brightness Ramp"), 0, brightramp_tip, m_brightramp, 0 ); MENUENT(mRep, ZTX("Color Ramp"), 0, color_ramp_tip, m_color_ramp, 0 ); MENUENT(mRep, ZTX("Match Colors"), 0, match_color_tip, m_match_color, 0 ); MENUENT(mRep, ZTX("Color Profile"), 0, color_profile_tip, m_color_profile, 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("Color Fringes"), 0, color_fringes_tip, m_color_fringes, 0 ); MENUENT(mRep, ZTX("Stuck Pixels"), 0, stuck_pixels_tip, m_stuck_pixels, 0 ); mBend = create_popmenu(); MENUENT(mBend, ZTX("Unbend"), 0, unbend_tip, m_unbend, 0 ); MENUENT(mBend, ZTX("Fix Perspective"), 0, perspective_tip, m_perspective, 0 ); MENUENT(mBend, ZTX("Warp area"), 0, warp_area_tip, m_warp_area, 0 ); MENUENT(mBend, ZTX("Warp curved"), 0, warp_curved_tip, m_warp_curved, 0 ); MENUENT(mBend, ZTX("Warp linear"), 0, warp_linear_tip, m_warp_linear, 0 ); MENUENT(mBend, ZTX("Warp affine"), 0, warp_affine_tip, m_warp_affine, 0 ); MENUENT(mBend, ZTX("Flatten Book Page"), 0, flatbook_tip, m_flatbook, 0 ); mEff = create_popmenu(); MENUENT(mEff, ZTX("Color Depth"), 0, colordep_tip, m_colordep, 0 ); MENUENT(mEff, ZTX("Sketch"), 0, sketch_tip, m_sketch, 0 ); MENUENT(mEff, ZTX("Line Drawing"), 0, linedraw_tip, m_linedraw, 0 ); MENUENT(mEff, ZTX("Color Drawing"), 0, colordraw_tip, m_colordraw, 0 ); MENUENT(mEff, ZTX("Graduated Blur"), 0, gradblur_tip, m_gradblur, 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("Make Waves"), 0, waves_tip, m_waves, 0); MENUENT(mEff, ZTX("Directed Blur"), 0, dirblur_tip, m_dirblur, 0); MENUENT(mEff, ZTX("Spherical Projection"), 0, sphere_tip, m_sphere, 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, STP_tip, m_STP, 0 ); MENUENT(mComb, ZTX("Stack/Noise"), 0, STN_tip, m_STN, 0 ); MENUENT(mComb, ZTX("Panorama"), 0, pano_tip, m_pano, 0 ); MENUENT(mComb, ZTX("Vertical Panorama"), 0, vpano_tip, m_vpano, 0 ); MENUENT(mComb, ZTX("PT Panorama"), 0, pano_PT_tip, m_pano_PT, 0 ); MENUENT(mComb, ZTX("Mashup"), 0, mashup_tip, m_mashup, 0 ); mTools = create_popmenu(); MENUENT(mTools, ZTX("Index Image Files"), 0, index_tip, m_index, 0 ); MENUENT(mTools, ZTX("User Options"), 0, options_tip, m_options, 0 ); MENUENT(mTools, ZTX("Keyboard Shortcuts"), 0, KBshortcuts_tip, m_KBshortcuts, 0 ); MENUENT(mTools, ZTX("Show Brightness Dist."), 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, darkbrite_tip, m_darkbrite, 0 ); MENUENT(mTools, ZTX("Monitor Color"), 0, moncolor_tip, m_moncolor, 0 ); MENUENT(mTools, ZTX("Monitor Gamma"), 0, mongamma_tip, m_mongamma, 0 ); MENUENT(mTools, ZTX("Change Language"), 0, changelang_tip, m_changelang, 0 ); MENUENT(mTools, ZTX("Missing Translations"), 0, untranslated_tip, m_untranslated, 0 ); MENUENT(mTools, ZTX("Calibrate Printer"), 0, calibrate_printer_tip, m_calibrate_printer, 0 ); MENUENT(mTools, ZTX("Resources"), 0, resources_tip, m_resources, 0 ); // MENUENT(mTools, ZTX("zappcrash test"), 0, zappcrash_tip, m_zappcrash, 0 ); mBatch = create_popmenu(); MENUENT(mBatch, ZTX("Batch Convert"), 0, batch_convert_tip, m_batch_convert, 0 ); MENUENT(mBatch, ZTX("Batch Upright"), 0, batch_upright_tip, m_batch_upright, 0 ); MENUENT(mBatch, ZTX("Batch Delete/Trash"), 0, batch_deltrash_tip, m_batch_deltrash, 0 ); MENUENT(mBatch, ZTX("Batch RAW (DCraw)"), 0, batch_dcraw_tip, m_batch_dcraw, 0 ); MENUENT(mBatch, ZTX("Batch RAW (Raw Therapee)"), 0, batch_rawtherapee_tip, m_batch_rawtherapee, 0 ); MENUENT(mBatch, ZTX("Script Files"), 0, scriptfiles_tip, m_scriptfiles, 0 ); MENUENT(mBatch, ZTX("Burn Images to CD/DVD"), 0, burn_tip, m_burn, 0 ); MENUENT(mBatch, ZTX("Find Duplicate Images"), 0, duplicates_tip, m_duplicates, 0 ); // duplicates of metadata menu MENUENT(mBatch, ZTX("Batch Add/Remove Tags"), 0, batch_tags_tip, m_batch_tags, 0 ); MENUENT(mBatch, ZTX("Batch Rename Tags"), 0, batch_renametags_tip, m_batch_renametags, 0 ); MENUENT(mBatch, ZTX("Batch Add/Change Metadata"), 0, batch_metadata_tip, m_batch_metadata, 0 ); MENUENT(mBatch, ZTX("Batch Add Geotags"), 0, batch_geotags_tip, m_batch_geotags, 0 ); MENUENT(mBatch, ZTX("Images by Geotag"), 0, geotag_groups_tip, m_geotag_groups, 0 ); MENUENT(mBatch, ZTX("Search Images"), 0, search_images_tip, m_search_images, 0 ); mAlbums = create_popmenu(); MENUENT(mAlbums, ZTX("Manage Albums"), 0, manage_albums_tip, m_manage_albums, 0 ); MENUENT(mAlbums, ZTX("Slide Show"), 0, slideshow_tip, m_slideshow, 0 ); MENUENT(mAlbums, ZTX("Set Desktop Wallpaper"), 0, setdesktop_tip, m_setdesktop, 0 ); MENUENT(mAlbums, ZTX("Cycle Desktop Wallpaper"), 0, cycledesktop_tip, m_cycledesktop, 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("Edit Functions Summary"), 0, edit_funcs_summary_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 ); // F view menu (buttons) MENUENT(0, ZTX("Image"), "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, 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"), "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, ZTX("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("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("Bend"), "bend.png", bend_tip, (cbFunc *) popup_menu, (cchar *) mBend); 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("Undo/Redo"), "undo_redo.png", undo_redo_tip, m_undo_redo, 0 ); 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, ZTX("Image"), "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, 0, "separator.png", 0, 0, 0 ); MENUENT(0, ZTX("Sync.G"), "sync.png", sync_tip, navi::menufuncx, 0 ); MENUENT(0, ZTX("Albums"), "albums.png", albums_tip, (cbFunc *) popup_menu, (cchar *) mAlbums); MENUENT(0, ZTX("Bookmarks"), "goto.png", bookmarks_tip, m_goto_bookmark, 0 ); 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+.png", previous_page_tip, navi::menufuncx, 0 ); MENUENT(0, ZTX("Page↓"), "down+.png", next_page_tip, navi::menufuncx, 0 ); MENUENT(0, ZTX("Scroll"), "scroll.png", slow_scroll_tip, navi::menufuncx, 0 ); MENUENT(0, ZTX("Batch"), "batch.png", batch_tip, (cbFunc *) popup_menu, (cchar *) mBatch); 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, ZTX("Image"), "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, 0, "separator.png", 0, 0, 0 ); MENUENT(0, ZTX("Choose Map"), "choosemap.png", load_geomap_tip, m_load_geomap, 0 ); MENUENT(0, ZTX("Search Range"), "setmaprange.png", mapsearch_tip, m_mapsearch_range, 0 ); MENUENT(0, ZTX("Images by Geotag"), "search.png", geotag_groups_tip, m_geotag_groups, 0 ); MENUENT(0, ZTX("Search Images"), "search.png", search_images_tip, m_search_images, 0 ); NWmenu = Nmenus; // dummy menus only for KB shortcuts MENUENT(0, ZTX("Undo"), "KB shortcut", 0, m_undo, 0 ); MENUENT(0, ZTX("Redo"), "KB shortcut", 0, m_redo, 0 ); // build the vertical menus for the F/G/W windows Vmenu *Fvm = Vmenu_new(Fmenu); Vmenu *Gvm = Vmenu_new(Gmenu); Vmenu *Wvm = Vmenu_new(Wmenu); Vmenu *Xvm; int iz = iconsize; 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; else if (me < NGmenu) Xvm = Gvm; else Xvm = Wvm; 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 if (strmatch(menutab[me].icon,"separator.png")) Vmenu_add(Xvm, 0, menutab[me].icon, iz, iz/3, 0, 0, 0); else if (strmatch(menu_style,"icons")) { if (menutab[me].icon) Vmenu_add(Xvm, 0, menutab[me].icon,iz,iz,menutab[me].desc, menutab[me].func, menutab[me].arg); else Vmenu_add(Xvm, menutab[me].menu, 0, 0, 0, menutab[me].desc, menutab[me].func, menutab[me].arg); } else /* both */ Vmenu_add(Xvm, menutab[me].menu, menutab[me].icon, iz, iz, menutab[me].desc, menutab[me].func, menutab[me].arg); } } // right-click popup menus -------------------------------------------- cchar *menupopimage = ZTX("Popup Image"); cchar *menupopimageadd = ZTX("Popup Image (add)"); cchar *menumetadata1 = ZTX("View Metadata"); cchar *menumetadata2 = ZTX("Edit Metadata"); cchar *menumetadata3 = ZTX("Edit Geotags"); cchar *menurename = ZTX("Rename"); cchar *menucopytoloc = ZTX("Copy to Location"); cchar *menumovetoloc = ZTX("Move to Location"); cchar *menucopytoclip = ZTX("Copy to Clipboard"); cchar *menuremovefromalbum = ZTX("Remove from Album"); 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 *menupastecurrfile = ZTX("Paste Current Image File Here"); cchar *menutrimrotate = ZTX("Trim/Rotate"); cchar *menuresize = ZTX("Resize"); cchar *menuupright = ZTX("Upright"); cchar *menuvoodoo = ZTX("Voodoo Enhance"); cchar *menucombo = ZTX("Retouch Combo"); cchar *menubrightdist = ZTX("Brightness Dist."); cchar *menuzonalflatten = ZTX("Zonal Flatten"); cchar *menutonemap = ZTX("Tone Mapping"); cchar *menuselect = ZTX("Select Area"); cchar *menuopenraw1 = ZTX("Open RAW file (ufraw)"); cchar *menuopenraw2 = ZTX("Open RAW file (Raw Therapee)"); cchar *menutrash = ZTX("Trash"); cchar *menudelete = Bdelete; popmenu_main = create_popmenu(); // main window image add_popmenu_item(popmenu_main,menumetadata1,popup_menufunc,"view metadata"); add_popmenu_item(popmenu_main,menumetadata2,popup_menufunc,"edit metadata"); add_popmenu_item(popmenu_main,menumetadata3,popup_menufunc,"edit geotags"); add_popmenu_item(popmenu_main,menurename,popup_menufunc,"rename"); add_popmenu_item(popmenu_main,menucopytoloc,popup_menufunc,"copytoloc"); add_popmenu_item(popmenu_main,menumovetoloc,popup_menufunc,"movetoloc"); add_popmenu_item(popmenu_main,menucopytocache,popup_menufunc,"copytocache"); add_popmenu_item(popmenu_main,menucopytoclip,popup_menufunc,"copytoclip"); add_popmenu_item(popmenu_main,menuupright,popup_menufunc,"upright"); add_popmenu_item(popmenu_main,menutrimrotate,popup_menufunc,"trim/rotate"); add_popmenu_item(popmenu_main,menuresize,popup_menufunc,"resize"); add_popmenu_item(popmenu_main,menuvoodoo,popup_menufunc,"voodoo"); add_popmenu_item(popmenu_main,menucombo,popup_menufunc,"combo"); add_popmenu_item(popmenu_main,menubrightdist,popup_menufunc,"brightness dist"); add_popmenu_item(popmenu_main,menuzonalflatten,popup_menufunc,"zonal flatten"); add_popmenu_item(popmenu_main,menutonemap,popup_menufunc,"tonemap"); add_popmenu_item(popmenu_main,menuselect,popup_menufunc,"select"); add_popmenu_item(popmenu_main,menutrash,popup_menufunc,"trash"); add_popmenu_item(popmenu_main,menudelete,popup_menufunc,"delete"); popmenu_thumb = create_popmenu(); // gallery thumbnail add_popmenu_item(popmenu_thumb,menupopimage,popup_menufunc,"popimage"); add_popmenu_item(popmenu_thumb,menupopimageadd,popup_menufunc,"popimageadd"); add_popmenu_item(popmenu_thumb,menumetadata1,popup_menufunc,"view metadata"); add_popmenu_item(popmenu_thumb,menurename,popup_menufunc,"rename"); add_popmenu_item(popmenu_thumb,menuupright,popup_menufunc,"upright thumb"); add_popmenu_item(popmenu_thumb,menucopytoloc,popup_menufunc,"copytoloc"); add_popmenu_item(popmenu_thumb,menumovetoloc,popup_menufunc,"movetoloc"); add_popmenu_item(popmenu_thumb,menucopytocache,popup_menufunc,"copytocache"); add_popmenu_item(popmenu_thumb,menucopytoclip,popup_menufunc,"copytoclip"); add_popmenu_item(popmenu_thumb,menutrash,popup_menufunc,"trash"); add_popmenu_item(popmenu_thumb,menudelete,popup_menufunc,"delete"); popmenu_raw = create_popmenu(); // gallery thumbnail, RAW file add_popmenu_item(popmenu_raw,menuopenraw1,m_ufraw,0); add_popmenu_item(popmenu_raw,menuopenraw2,m_rawtherapee,0); add_popmenu_item(popmenu_raw,menumetadata1,popup_menufunc,"view metadata"); add_popmenu_item(popmenu_raw,menurename,popup_menufunc,"rename"); add_popmenu_item(popmenu_raw,menucopytoloc,popup_menufunc,"copytoloc"); add_popmenu_item(popmenu_raw,menumovetoloc,popup_menufunc,"movetoloc"); add_popmenu_item(popmenu_raw,menutrash,popup_menufunc,"trash"); add_popmenu_item(popmenu_raw,menudelete,popup_menufunc,"delete"); popmenu_album = create_popmenu(); // album gallery thumbnail add_popmenu_item(popmenu_album,menucopytocache,popup_menufunc,"copytocache"); add_popmenu_item(popmenu_album,menucuttocache,popup_menufunc,"cuttocache"); add_popmenu_item(popmenu_album,menucopytoclip,popup_menufunc,"copytoclip"); add_popmenu_item(popmenu_album,menupastecachekeep,popup_menufunc,"pastecachekeep"); add_popmenu_item(popmenu_album,menupastecachehere,popup_menufunc,"pastecachehere"); add_popmenu_item(popmenu_album,menupastecurrfile,popup_menufunc,"pastecurrfile"); add_popmenu_item(popmenu_album,menuremovefromalbum,popup_menufunc,"removefromalbum"); add_popmenu_item(popmenu_album,menupopimage,popup_menufunc,"popimage"); add_popmenu_item(popmenu_album,menupopimageadd,popup_menufunc,"popimageadd"); add_popmenu_item(popmenu_album,menumetadata1,popup_menufunc,"view metadata"); return; } /**************************************************************************/ // set window view mode, F/G/W void m_viewmode(GtkWidget *, cchar *fgw) { zthreadcrash(); if (FGW == *fgw) return; // no change if (*fgw == 'F') // set F view mode for image file { F1_help_topic = "file_view"; gtk_widget_hide(Ghbox); gtk_widget_hide(Whbox); gtk_widget_show_all(Fhbox); set_mwin_title(); FGW = 'F'; Cstate = &Fstate; // set drawing area Cdrawin = Fdrawin; gdkwin = gtk_widget_get_window(Fdrawin); // GDK window } if (*fgw == 'G') // set G view mode for thumbnail gallery { if (CEF) return; // don't interrupt edit func. 15.04 if (Fslideshow) return; // no crash slide show 15.02 gtk_widget_hide(Fhbox); gtk_widget_hide(Whbox); gtk_widget_show_all(Ghbox); FGW = 'G'; static int ftf = 1; // required to avoid lack of scroll for the if (ftf) zmainloop(); // first time gallery is painted (GTK bug?) ftf = 0; Cstate = 0; // no F/W image drawing area Cdrawin = 0; gdkwin = 0; if (curr_file) gallery(curr_file,"paint"); // set gallery posn. at curr. file else gallery(0,"paint",-1); // else leave unchanged } if (*fgw == 'W') // set W view mode for geomaps { if (CEF) return; // don't interrupt edit func. 15.04 if (Fslideshow) return; // no crash slide show 15.10 F1_help_topic = "world_maps_view"; gtk_widget_hide(Fhbox); gtk_widget_hide(Ghbox); gtk_widget_show_all(Whbox); FGW = 'W'; Cstate = &Wstate; // set drawing area Cdrawin = Wdrawin; gdkwin = gtk_widget_get_window(Wdrawin); // GDK window gtk_window_set_title(MWIN,ZTX("Image Locations")); // window title Fpaintnow(); if (! Wstate.fpxb) m_load_geomap(0,"default"); // no map loaded, load default map } return; } // right-click popup menu function void popup_menufunc(GtkWidget *, cchar *menu) { if (strmatch(menu,"popimage")) popimage(0); // funcs for main and gallery windows if (strmatch(menu,"popimageadd")) popimage(1); if (strmatch(menu,"view metadata")) meta_view(1); if (strmatch(menu,"edit metadata")) m_edit_metadata(0,0); if (strmatch(menu,"edit geotags")) m_edit_geotags(0,0); if (strmatch(menu,"rename")) m_rename(0,0); // these funcs use clicked_file if defined, if (strmatch(menu,"upright thumb")) m_upright(0,0); // else they use curr_file. if (strmatch(menu,"copytoloc")) m_copytoloc(0,0); // set clicked_file to null after use. if (strmatch(menu,"movetoloc")) m_movetoloc(0,0); if (strmatch(menu,"delete")) m_delete(0,0); if (strmatch(menu,"trash")) m_trash(0,0); if (strmatch(menu,"copytoclip")) m_copyto_clip(0,0); if (strmatch(menu,"copytocache")) m_copyto_cache(0,0); if (strmatch(menu,"removefromalbum")) album_remove(0,0); // funcs for album gallery if (strmatch(menu,"cuttocache")) album_cutfile(0,0); // depend on clicked_file being defined if (strmatch(menu,"pastecachehere")) album_pastecache(0,"clear"); if (strmatch(menu,"pastecachekeep")) album_pastecache(0,"keep"); if (strmatch(menu,"pastecurrfile")) album_pastecurrfile(0,0); if (strmatch(menu,"trim/rotate")) m_trimrotate(0,0); // functions using curr_file only if (strmatch(menu,"resize")) m_resize(0,0); // (not used for gallery/thumbnail click) if (strmatch(menu,"upright")) m_upright(0,0); if (strmatch(menu,"voodoo")) m_voodoo1(0,0); if (strmatch(menu,"combo")) m_combo(0,0); if (strmatch(menu,"brightness dist")) m_britedist(0,0); if (strmatch(menu,"zonal flatten")) m_zonal_flatten(0,0); if (strmatch(menu,"tonemap")) m_tonemap(0,0); if (strmatch(menu,"select")) m_select(0,0); return; } // main window mouse right-click popup menu void image_Rclick_popup() { if (! curr_file) return; popup_menu(Mwin,popmenu_main); return; } // gallery thumbnail mouse left-click function // open the clicked file in view mode F void gallery_Lclick_func(int Nth) { char *file; if (checkpend("block")) return; // 15.08 file = gallery(0,"find",Nth); if (! file) return; f_open(file,Nth,0,1); // clicked file >> current file zfree(file); m_viewmode(0,"F"); return; } // gallery thumbnail mouse right-click popup menu void gallery_Rclick_popup(int Nth) { int ftyp; clicked_posn = Nth; // clicked gallery position (0 base) clicked_file = gallery(0,"find",Nth); // clicked_file is subject for zfree() if (! clicked_file) return; ftyp = image_file_type(clicked_file); if (navi::gallerytype == 4) // gallery type is album popup_menu(Mwin,popmenu_album); else if (ftyp == 2) // clicked thumbnail is an image file popup_menu(Mwin,popmenu_thumb); else if (ftyp == 3) // clicked thumbnail is a RAW file popup_menu(Mwin,popmenu_raw); 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,"F",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; m_viewmode(0,"F"); for (ii = 0; ii < Nmenus; ii++) { if (! menutab[ii].menu) continue; // 15.07 if (strmatchcase(ZTX(menu),ZTX(menutab[ii].menu))) break; } if (ii < Nmenus) menutab[ii].func(0,menu); return; } fotoxx-15.11.1/f.bend.cc0000664000175000017500000027023712616075370013336 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** Fotoxx image edit - bend (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_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 ***************************************************************************/ #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; 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); // 15.09 PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); /*** ___________________________________ | | | Unbend | | | | linear curved | | vertical [__|v] [__|v] | | horizontal [__|v] [__|v] | | | | [grid] | | [done] [cancel] | |___________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Unbend"),Mwin,Bdone,Bcancel,null); EFunbend.zd = 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,"vbox","vb3","hb1",0,"homog|space=5"); zdialog_add_widget(zd,"label","labspace","vb1",""); zdialog_add_widget(zd,"label","labvert","vb1",ZTX("vertical")); zdialog_add_widget(zd,"label","labhorz","vb1",ZTX("horizontal")); zdialog_add_widget(zd,"label","lablin","vb2",ZTX("linear")); zdialog_add_widget(zd,"spin","splinvert","vb2","-99|99|1|0"); zdialog_add_widget(zd,"spin","splinhorz","vb2","-99|99|1|0"); zdialog_add_widget(zd,"label","labhorz","vb3",ZTX("curved")); zdialog_add_widget(zd,"spin","spcurvert","vb3","-99|99|1|0"); zdialog_add_widget(zd,"spin","spcurhorz","vb3","-99|99|1|0"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"button","grid","hb2",Bgrid,"space=10"); 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 = 1; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); signal_thread(); return 1; } if (zd->zstat) // dialog complete { currgrid = 0; // restore normal grid settings 15.09 if (zd->zstat == 1) { // 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); // discard edit draw_toplines(2); // 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(); } if (strmatch(event,"grid")) m_gridlines(0,"grid 2"); // grid settings dialog 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; 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); } 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); // 15.09 PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); PSP_npix = 0; // no pixels yet zdialog *zd = zdialog_new(ZTX("Perspective Correction"),Mwin,Bapply,Breset,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; if (strmatch(event,"done")) zd->zstat = 3; // from edit_setup() or f_save() if (strmatch(event,"enter")) zd->zstat = 3; // KB input if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open() if (strmatch(event,"escape")) zd->zstat = 4; // escape = cancel 15.07 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) { // done erase_toptext(102); // erase points edit_done(0); // commit edit } else { // cancel erase_toptext(102); // erase points 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(); 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,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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); 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; // bugfix 15.01.1 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); 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_blendwidth 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 = 1.0 * dist / sa_blend; // pixel is blended 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); 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); } 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); // 15.09 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,"spin","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,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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); } 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); // 15.09 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,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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 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); // 15.09 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,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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); // bugfix 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 } 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 Photo"); 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 Photo | | | | 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,"enter")) zd->zstat = 1; // KB input if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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; // bugfix 15.09 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; // 15.09 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); // paint modified areas if (Bnm) Fpaint3(0,bottmin,ww,bottmax-bottmin); 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 15.09 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; } fotoxx-15.11.1/f.file.cc0000664000175000017500000042334412616075370013344 0ustar micomico/************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2015 Michael Cornelison Source URL: http://kornelix.com/fotoxx 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. *************************************************************************** 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_open_drag open drag/drop image file m_previous open the previously opened image file m_ufraw open a camera RAW file using the Ufraw program m_rawtherapee open a camera RAW file using the RawTherapee program 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 m_create create a new monocolor image create_blank_file callable function to create a new monocolor image m_rename rename current image file or clicked thumbnail m_copytoloc copy an image file to a new location (duplicate) m_movetoloc move an image file to a new location copy_move callable function to copy or move an image file m_copyto_clip copy clicked file or current file to the clipboard file_copytoclipboard copy image file to GTK clipboard m_trash move an image file to trash location m_delete delete 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 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 m_prev open previous file in current gallery m_next open next file in current gallery m_prev_next open previous or next file in current gallery 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) ***************************************************************************/ #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 = 5; // gallery type = recent files gallery(recentfiles_file,"initF"); // generate gallery of recent files m_viewmode(0,"G"); gallery(0,"paint",0); return; } // add a new file to the list of recent files, first position void add_recent_file(cchar *newfile) { FILE *fidr, *fidw; char *pp, tempfile[200], buff[XFCC]; int ii, err; strcpy(tempfile,recentfiles_file); strcat(tempfile,"_temp"); fidw = fopen(tempfile,"w"); // open output temp file if (! fidw) { zmessageACK(Mwin,"file error: %s",strerror(errno)); return; } fprintf(fidw,"%s\n",newfile); // add new file as first in list fidr = fopen(recentfiles_file,"r"); if (fidr) { for (ii = 0; ii < 100; ii++) { // read list of recent files <= 100 pp = fgets_trim(buff,XFCC,fidr,1); if (! pp) break; if (strmatch(pp,newfile)) continue; // skip new file if present if (*pp == '/') fprintf(fidw,"%s\n",pp); // write to output file } fclose(fidr); } fclose(fidw); err = rename(tempfile,recentfiles_file); if (err) zmessageACK(Mwin,"file error: %s",strerror(errno)); 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) // 15.05 { 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, cc, err, sort, Nxrec, ftf; sxrec_t sxrec; FILE *fid; NFxrec_t *NFxrec = 0; cc = maximages * sizeof(NFxrec_t); // allocate memory NFxrec = (NFxrec_t *) zmalloc(cc); Nxrec = 0; ftf = 1; 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); while (true) { err = read_sxrec_seq(sxrec,ftf); // read image index recs. if (err) break; ii = Nxrec; if (sort == 1) { // 15.07 if (strmatch(sxrec.pdate,"null")) continue; // use EXIF photo date strncpy0(NFxrec[ii].date,sxrec.pdate,16); // ignore images without photo date } else strncpy0(NFxrec[ii].date,sxrec.fdate,16); // else use file mod date NFxrec[ii].file = sxrec.file; // image file zfree(sxrec.tags); // free unused stuff zfree(sxrec.capt); zfree(sxrec.comms); zfree(sxrec.gtags); if (++Nxrec == maximages) { zmessageACK(Mwin,"too many image files"); goto cleanup; } } 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: for (ii = 0; ii < Nxrec; ii++) // free memory zfree(NFxrec[ii].file); zfree(NFxrec); free_resources(); navi::gallerytype = 6; // newest files gallery(searchresults_file,"initF"); // generate gallery of matching files m_viewmode(0,"G"); gallery(0,"paint",0); 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 *) { F1_help_topic = "open_image_file"; if (checkpend("busy block mods")) return; // 15.11 f_open(null); return; } // open drag-drop file void m_open_drag(int x, int y, char *file) { F1_help_topic = "open_image_file"; if (checkpend("busy block mods")) return; // 15.11 f_open(file); return; } /**************************************************************************/ // open the previous file opened (not the same as toolbar [prev] button) // repeated use will cycle back and forth between two most recent files void m_previous(GtkWidget *, cchar *menu) { FILE *fid; char *file, buff[XFCC]; int Nth = 0, err; float gzoom; F1_help_topic = "open_previous_file"; if (checkpend("busy block mods")) return; // 15.11 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; } /**************************************************************************/ // menu function: open a camera RAW file and edit with the ufraw GUI // opens 'clicked_file' if present or 'rawfile' if not void m_ufraw(GtkWidget *, cchar *menu) { char *pp; char *tiffile; int err; STATB statb; cchar *ufrawcommand = "ufraw --exposure=auto --interpolation=bilinear" " --out-type=tiff --out-depth=16 --overwrite" " --output=\"%s\" \"%s\""; F1_help_topic = "open_raw_file"; if (! Fufraw) { zmessageACK(Mwin,ZTX("UFraw not installed")); return; } if (checkpend("busy block mods")) return; // 15.11 if (rawfile) zfree(rawfile); if (clicked_file) { rawfile = clicked_file; clicked_file = 0; } if (! rawfile) { pp = curr_file; // directory for file open dialog if (! pp) pp = curr_dirk; rawfile = zgetfile(ZTX("Open RAW file (ufraw)"),MWIN,"file",pp); // dialog to get RAW filespec if (! rawfile) return; } if (image_file_type(rawfile) != 3) { zmessageACK(Mwin,ZTX("RAW type not registered in User Settings")); zfree(rawfile); rawfile = 0; return; } zmainloop(); tiffile = raw_to_tiff(rawfile); shell_quiet(ufrawcommand,tiffile,rawfile); // start ufraw command, convert to tiff zfree(rawfile); rawfile = 0; err = stat(tiffile,&statb); if (err) { zfree(tiffile); printz("ufraw produced no tif-16 file\n"); return; } err = f_open(tiffile,0,0,1); // open tiff file if (err) { zfree(tiffile); return; } update_image_index(tiffile); // update index rec. 15.03 zfree(tiffile); 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, *temp; int err; STATB statb; cchar *command = "rawtherapee -o \"%s\" -t -Y \"%s\" "; F1_help_topic = "open_raw_file"; if (! Frawtherapee) { zmessageACK(Mwin,ZTX("Raw Therapee not installed")); return; } if (checkpend("busy block mods")) return; // 15.11 if (rawfile) zfree(rawfile); if (clicked_file) { rawfile = clicked_file; clicked_file = 0; } if (! rawfile) { pp = curr_file; // directory for file open dialog if (! pp) pp = curr_dirk; rawfile = zgetfile(ZTX("Open RAW file (Raw Therapee)"),MWIN,"file",pp); if (! rawfile) return; } if (image_file_type(rawfile) != 3) { zmessageACK(Mwin,ZTX("RAW type not registered in User Settings")); zfree(rawfile); rawfile = 0; return; } zmainloop(); tiffile = raw_to_tiff(rawfile); shell_quiet(command,tiffile,rawfile); // start command, convert to tiff zfree(rawfile); rawfile = 0; temp = zstrdup(tiffile); // Raw Therapee ignores specified .tif pp = strstr(temp,".tif"); // and outputs .tif instead pp[4] = 0; rename(temp,tiffile); zfree(temp); err = stat(tiffile,&statb); if (err) { zfree(tiffile); printz("Raw Therapee produced no tif-16 file\n"); return; } err = f_open(tiffile,0,0,1); // open tiff file if (err) { zfree(tiffile); return; } update_image_index(tiffile); // update index rec. 15.03 zfree(tiffile); return; } /**************************************************************************/ // 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, ftyp, retcode = 0; static int Freent = 0; char *pp, *file; void (*menufunc)(GtkWidget *, cchar *); STATB statb; if (Freent++) { // stop re-entry printz("f_open() re-entry \n"); goto ret1; } if (clicked_file) zfree(clicked_file); clicked_file = 0; 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; // 15.11 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; } ftyp = image_file_type(file); // must be image or RAW file type if (ftyp == 4) { if (Fack) zmessageACK(Mwin,"thumbnail file"); goto ret4; } if (ftyp != 2 && ftyp != 3) { // must be supported image file type if (Fack) zmessageACK(Mwin,ZTX("unknown file type")); // or RAW 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 15.03 fposn = gallery_position(file,Nth); // file position in gallery list if (fposn < 0) { // not there gallery(curr_file,"init"); // generate new gallery list fposn = gallery_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) copy_move(0); // " copy/move dialog if (zddelete) m_delete(0,0); // " delete dialog if (zdtrash) m_trash(0,0); // " trash dialog if (zdexifview) meta_view(0); // " EXIF/IPTC data view window if (zdeditmeta) m_edit_metadata(0,0); // " edit metadata dialog if (zdexifedit) m_meta_edit_any(0,0); // " edit any metadata dialog if (zdeditgeotags) m_edit_geotags(0,0); // " edit geotags dialog if (Fcaptions) m_captions(0,0); // show caption/comments at top m_viewmode(0,"F"); // insure F mode if (menufunc) menufunc(0,0); // restart edit function 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 (E0pxm) { // edits were made PXB_free(Fpxb); // new window image Fpxb = PXM_PXB_copy(E0pxm); } if (! f_save_file) return 1; // bugfix v.15.10 if (curr_file) zfree(curr_file); curr_file = zstrdup(f_save_file); // new current file fposn = gallery_position(curr_file,Nth); // file position in gallery list if (fposn < 0) { // not there gallery(curr_file,"init"); // generate new gallery list fposn = gallery_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 gtk_window_present(MWIN); 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. int preload_lock = 0; void f_preload(int next) // revised 15.01 { void * preload_thread(void *); char *file = 0; if (! curr_file) return; file = gallery_getnext(next,Flastversion); if (! file) return; if (strmatch(file,curr_file)) { zfree(file); return; } while (preload_lock) zsleep(0.01); // wait if preload thread busy 15.02 preload_lock = 1; start_detached_thread(preload_thread,file); return; } void * preload_thread(void *arg) { int fd; char *file = (char *) arg; 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); preload_lock = 0; // 15.02 pthread_exit(0); } /**************************************************************************/ // create a new blank image with desired background color void m_create(GtkWidget *, cchar *pname) { int create_dialog_event(zdialog *zd, cchar *event); 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,"entry","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,"spin","width","hbz","100|20000|1|1600"); zdialog_add_widget(zd,"label","space","hbz",0,"space=5"); zdialog_add_widget(zd,"label","labh","hbz",Bheight,"space=5"); zdialog_add_widget(zd,"spin","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_run(zd,create_dialog_event); // 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")); // 15.03 zdialog_free(zd); Fblock = 0; m_create(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; } // dialog event and completion function int create_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 return 1; } // 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 if (! tempxb) zappcrash("out of memory"); 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; } /**************************************************************************/ // 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; if (menu) 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] | | | | [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,"entry","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_run(zd,rename_dialog_event); // 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) gtk_window_present(MWIN); // keep focus on main window 15.03 return; } // dialog event and completion callback function int rename_dialog_event(zdialog *zd, cchar *event) { char *pp, *pdir, *pfile, *pext; char *newfile, *thumbfile, *nextfile; int nseq, digits, ccp, ccn, ccx, err, fnew; STATB statb; if (strmatch(event,"enter")) zd->zstat = 1; // [apply] if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"Bprev")) // previous name >> new name if (strlen(rename_prev) > 0) zdialog_stuff(zd,"newname",rename_prev); if (strmatch(event,"Badd1")) // increment sequence number { zdialog_fetch(zd,"newname",rename_new,194); // get entered filename pp = rename_new + strlen(rename_new); digits = 0; while (pp[-1] >= '0' && pp[-1] <= '9') { pp--; // look for NNN in filenameNNN digits++; } nseq = 1 + atoi(pp); // NNN + 1 if (nseq > 9999) nseq = 0; if (digits < 2) digits = 2; // keep digit count if enough if (nseq > 99 && digits < 3) digits = 3; // use leading zeros if (nseq > 999 && digits < 4) digits = 4; snprintf(pp,digits+1,"%0*d",digits,nseq); zdialog_stuff(zd,"newname",rename_new); } if (zd->zstat == 0) return 1; // not finished if (zd->zstat != 1) { // canceled if (rename_file) zfree(rename_file); rename_file = 0; zdialog_free(zd); // kill dialog zdrename = 0; return 1; } zd->zstat = 0; // apply - keep dialog active if (! rename_file) return 1; if (checkpend("all")) return 1; // 15.11 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); return 1; } if (FGW == 'F') nextfile = gallery(0,"find",curr_file_posn+1); // save next file, before rename 15.03 else nextfile = 0; err = rename(rename_file,newfile); // rename the file if (err) { zmessageACK(Mwin,"file error: %s",strerror(errno)); zdrename = 0; zfree(newfile); zfree(rename_file); rename_file = 0; zdialog_free(zd); return 1; } thumbfile = image_thumbfile(newfile,&fnew); // remove stale thumbnail if (fnew != 1) remove(thumbfile); zfree(thumbfile); strncpy0(rename_prev,rename_new,199); // save new name to previous name load_filemeta(newfile); // add new file to image index update_image_index(newfile); delete_image_index(rename_file); // delete old file in image index add_recent_file(newfile); // first in recent files list if (curr_file && strmatch(rename_file,curr_file)) // current file exists no more 15.11.1 free_resources(); zfree(rename_file); // 15.11.1 rename_file = 0; if (navi::gallerytype == 1) { // refresh gallery list 15.11 gallery(0,"init"); gallery(0,"paint",-1); } if (FGW == 'F' && nextfile) { f_open(nextfile); // will call m_rename() zfree(nextfile); } else { zdialog_free(zd); // 15.11.1 zdrename = 0; } zfree(newfile); // 15.11.1 return 1; } /**************************************************************************/ // right-click dropdown menu functions // copy an image file to a new location (duplicate) void m_copytoloc(GtkWidget *, cchar *) { copy_move("copy"); return; } // move an image file to a new location (delete original) void m_movetoloc(GtkWidget *, cchar *) { copy_move("move"); return; } // copy an image to a new location and optionally delete the original char *copymove_loc = 0; char *copymove_file = 0; int copymove_Fdelete; void copy_move(cchar *menu) { int copymove_dialog_event(zdialog *zd, cchar *event); cchar *copytitle = ZTX("Copy Image File"); cchar *movetitle = ZTX("Move Image File"); static char menux[8]; if (menu) strcpy(menux,menu); // remember for re-entry with null menu F1_help_topic = "copy_move_image"; 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; /*** XXXX Image File Image File: [________________] new location [______________] [browse] [apply] [cancel] ***/ if (! zdcopymove) { if (strmatch(menux,"copy")) zdcopymove = zdialog_new(copytitle,Mwin,Bapply,Bcancel,null); else zdcopymove = zdialog_new(movetitle,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,"entry","newloc","hb2",0,"expand"); zdialog_add_widget(zd,"button","browse","hb2",Bbrowse,"space=5"); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_resize(zd,400,0); zdialog_run(zd,copymove_dialog_event); // run dialog } zdialog *zd = zdcopymove; if (strmatch(menux,"copy")) zdialog_set_title(zd,copytitle); else if (strmatch(menux,"move")) zdialog_set_title(zd,movetitle); char *pfile = strrchr(copymove_file,'/'); if (pfile) pfile++; else pfile = copymove_file; zdialog_stuff(zd,"file",pfile); if (copymove_loc) zdialog_stuff(zd,"newloc",copymove_loc); if (strmatch(menux,"copy")) copymove_Fdelete = 0; // copy, no delete else copymove_Fdelete = 1; // move, delete original if (FGW == 'F') gtk_window_present(MWIN); // keep focus on main window 15.03 return; } // dialog event and completion callback function int copymove_dialog_event(zdialog *zd, cchar *event) { char *newloc, *pfile, *newfile = 0, *nextfile = 0; int cc, err, Nth; STATB statb; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 if (strmatch(event,"browse")) { // browse for new location if (! copymove_loc) { copymove_loc = (char *) zmalloc(XFCC); // 15.08 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 (zd->zstat == 0) return 1; // wait for dialog finished if (zd->zstat != 1) { // canceled 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; // 15.11 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 = shell_ack("cp -p \"%s\" \"%s\"",copymove_file,newfile); // copy source file to new file if (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 (FGW == 'F') { f_open(copymove_file); Nth = curr_file_posn + 1; nextfile = gallery(0,"find",Nth); } if (copymove_Fdelete) { // move - delete source file err = remove(copymove_file); if (err) { zmessageACK(Mwin,ZTX("delete failed: \n %s"),wstrerror(err)); zfree(newfile); zdialog_free(zd); zdcopymove = 0; return 1; } delete_image_index(copymove_file); // delete in image index } if (FGW == 'F') { // F view 15.03 if (nextfile) f_open(nextfile); // open next file else { // end of gallery if (copymove_Fdelete) free_resources(); // no curr. file zdialog_free(zd); // kill dialog zdcopymove = 0; } } if (navi::gallerytype == 1) { // refresh gallery 15.03 gallery(0,"init"); gallery(0,"paint",-1); } if (nextfile) zfree(nextfile); if (newfile) zfree(newfile); return 1; } /**************************************************************************/ // 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 *) { if (clicked_file) { file_copytoclipboard(clicked_file); zfree(clicked_file); clicked_file = 0; } else if (curr_file) file_copytoclipboard(curr_file); 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; } /**************************************************************************/ // Trash image file - move curr_file to trash. // Use the Linux standard trash function. // If not available, revert to Desktop folder. char *trash_file = 0; void m_trash(GtkWidget *, cchar *) { int trash_dialog_event(zdialog *zd, cchar *event); cchar *title = ZTX("Trash Image File"); cchar *autostep = ZTX("(automatic step to next image)"); F1_help_topic = "trash_image"; if (trash_file) zfree(trash_file); trash_file = 0; if (clicked_file) { // use clicked file if present trash_file = clicked_file; clicked_file = 0; } else if (curr_file) // else use current file trash_file = zstrdup(curr_file); else return; if (checkpend("all")) return; /*** ______________________________________ | Trash Image File | | | | Image File: [______________________] | | | | (automatic step to next image) | | | | [trash] [keep] [cancel] | |______________________________________| ***/ if (! zdtrash) // start dialog if not already { zdtrash = zdialog_new(title,Mwin,Btrash,Bkeep,Bcancel,null); zdialog *zd = zdtrash; 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"); if (FGW == 'F') zdialog_add_widget(zd,"label","labnext","dialog",autostep); zdialog_resize(zd,300,0); zdialog_run(zd,trash_dialog_event); // run dialog } char *pfile = strrchr(trash_file,'/'); if (pfile) pfile++; else pfile = trash_file; zdialog_stuff(zdtrash,"file",pfile); gtk_window_present(MWIN); // keep focus on main window 15.03 return; } // dialog event and completion callback function int trash_dialog_event(zdialog *zd, cchar *event) { int err = 0, yn, nn, Nth, gstat; char *file, dtdirk[200], trashdir[200]; GError *gerror = 0; GFile *gfile = 0; STATB statb; static int trashworks = 1, desktopworks = 1; // assume OK until found otherwise FILE *fid; if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 cchar *mess1 = ZTX("Linux standard trash does not work. \n" "Desktop trash folder will be created."); cchar *mess2 = ZTX("Linux and Desktop trash do not work. \n" "Permanently delete the image file?"); cchar *mess3 = ZTX("Cannot create trash folder: %s"); if (zd->zstat == 0) return 1; // wait for dialog finished if (zd->zstat == 2) goto NEXT; // [keep] button if (zd->zstat != 1) goto KILL; // [cancel] or [x] if (! trash_file) goto KILL; // [trash] button err = stat(trash_file,&statb); // get file status if (err) goto KILL; if (! S_ISREG(statb.st_mode)) goto KILL; if (! (statb.st_mode & S_IWUSR)) { // check permission yn = zmessageYN(Mwin,ZTX("Move read-only file to trash?")); if (! yn) goto NEXT; // keep file statb.st_mode |= S_IWUSR; // make writable if needed chmod(trash_file,statb.st_mode); } if (checkpend("all")) return 1; // 15.11 if (trashworks) // try Linux standard trash { gfile = g_file_new_for_path(trash_file); gstat = g_file_trash(gfile,0,&gerror); // move file to trash g_object_unref(gfile); if (! gstat) { printz("standard trash failed: %s \n",gerror->message); // did not work zmessageACK(Mwin,mess1); trashworks = 0; } } if (! trashworks && desktopworks) { fid = popen("xdg-user-dir DESKTOP","r"); // get desktop for user locale if (! fid) { zmessLogACK(Mwin,mess3,strerror(errno)); desktopworks = 0; } else { nn = fscanf(fid,"%s",dtdirk); pclose(fid); if (nn != 1) { zmessLogACK(Mwin,mess3,strerror(errno)); desktopworks = 0; } } if (desktopworks) { snprintf(trashdir,200,"%s/fotoxx-trash",dtdirk); // check if trash directory exists err = stat(trashdir,&statb); if (! S_ISDIR(statb.st_mode)) { err = mkdir(trashdir,0750); // create if not already if (err) { zmessLogACK(Mwin,mess3,strerror(errno)); desktopworks = 0; } } } if (desktopworks) { err = shell_ack("cp \"%s\" \"%s\" ",trash_file,trashdir); // copy image file to desktop trash if (err) desktopworks = 0; else { err = remove(trash_file); // remove original file if (err) { zmessLogACK(Mwin,ZTX("error: %s"),wstrerror(errno)); // cannot delete file goto KILL; } } } } if (! trashworks && ! desktopworks) { yn = zmessageYN(Mwin,mess2); // nothing works, ask to delete if (! yn) goto NEXT; // keep file err = remove(trash_file); // delete file if (err) { zmessLogACK(Mwin,ZTX("delete failed: \n %s"),wstrerror(errno)); goto KILL; } } delete_image_index(trash_file); // delete file in image index Nth = gallery_position(trash_file,0); // find in gallery list if (Nth >= 0) gallery(0,"delete",Nth); // delete from gallery list if (curr_file && strmatch(trash_file,curr_file)) // current file was trashed free_resources(); if (FGW == 'F') { // F window file = gallery(0,"find",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); } zd->zstat = 0; gtk_window_present(MWIN); // keep focus on main window return 1; } NEXT: if (FGW != 'F') goto KILL; // G gallery window m_next(0,0); // open next image gtk_window_present(MWIN); // keep focus on main window zd->zstat = 0; // keep dialog active return 1; KILL: if (trash_file) zfree(trash_file); // free memory trash_file = 0; zdialog_free(zd); // kill dialog zdtrash = 0; return 1; } /**************************************************************************/ // delete an image file, cannot be reversed char *delete_file = 0; void m_delete(GtkWidget *, cchar *) { int delete_dialog_event(zdialog *zd, cchar *event); cchar *title = ZTX("Delete Image File - CANNOT BE REVERSED"); cchar *autostep = ZTX("(automatic step to next image)"); F1_help_topic = "delete_image"; if (delete_file) zfree(delete_file); delete_file = 0; if (clicked_file) { // use clicked file if present delete_file = clicked_file; clicked_file = 0; } else if (curr_file) // else use current file delete_file = zstrdup(curr_file); else return; if (checkpend("all")) return; /*** ______________________________________ | Delete Image File | | CANNOT BE REVERSED | | | | Image File: [______________________] | | | | (automatic step to next image) | | | | [delete] [keep] [cancel] | |______________________________________| ***/ if (! zddelete) { zddelete = zdialog_new(title,Mwin,Bdelete,Bkeep,Bcancel,null); zdialog *zd = zddelete; 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"); if (FGW == 'F') zdialog_add_widget(zd,"label","labnext","dialog",autostep); zdialog_resize(zd,400,0); zdialog_run(zd,delete_dialog_event); // run dialog } char *pfile = strrchr(delete_file,'/'); if (pfile) pfile++; else pfile = delete_file; zdialog_stuff(zddelete,"file",pfile); gtk_window_present(MWIN); // keep focus on main window 15.03 return; } // dialog event and completion callback function int delete_dialog_event(zdialog *zd, cchar *event) { int err, Nth, yn; char *file; STATB statb; if (strmatch(event,"escape")) zd->zstat = 3; // escape = cancel 15.07 if (zd->zstat == 0) return 1; // wait for dialog finished if (zd->zstat == 2) goto NEXT; // [keep] button if (zd->zstat != 1) goto KILL; // [cancel] or [x] if (! delete_file) goto KILL; // [delete] button err = stat(delete_file,&statb); // file exists? if (err) goto KILL; if (! S_ISREG(statb.st_mode)) goto KILL; if (! (statb.st_mode & S_IWUSR)) { // check permission yn = zmessageYN(Mwin,ZTX("Delete read-only file?")); if (! yn) goto NEXT; // keep file statb.st_mode |= S_IWUSR; // make writable if needed chmod(delete_file,statb.st_mode); } if (checkpend("all")) return 1; // 15.11 err = remove(delete_file); // delete file if (err) { zmessageACK(Mwin,ZTX("delete failed: \n %s"),wstrerror(err)); goto KILL; } delete_image_index(delete_file); // delete old file in image index Nth = gallery_position(delete_file,0); // delete from gallery list if (Nth >= 0) gallery(0,"delete",Nth); if (curr_file && strmatch(delete_file,curr_file)) { // current file was deleted last_file_posn = curr_file_posn; // remember for prev/next use 15.05 free_resources(); } if (FGW == 'F') { // F window file = gallery(0,"find",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); } zd->zstat = 0; gtk_window_present(MWIN); // keep focus on main window return 1; } NEXT: if (FGW != 'F') goto KILL; m_next(0,0); // open next image gtk_window_present(MWIN); // keep focus on main window zd->zstat = 0; // keep dialog active return 1; KILL: if (delete_file) zfree(delete_file); // free memory delete_file = 0; zdialog_free(zd); // kill dialog zddelete = 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 *) { F1_help_topic = "print_image"; 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 save_params(); // save state for next session zdialog_inputs("save"); // save dialog inputs zdialog_positions("save"); // save dialog positions free_resources(); // delete temp files fflush(null); // flush stdout, stderr exif_server(0,0,0); // kill exif_server process shell_quiet("rm -R -f %s",tempdir); // delete temp files 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 | | | | [version] save as new file version | | [new file] save as new file name or type | | [replace] 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,"image","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; int err; char event2[12]; if (strmatch(event,"escape")) zd->zstat = 1; // escape = cancel 15.07 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; // 15.11 if (strmatch(event2,"newvers")) { 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); } 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; } /**************************************************************************/ // 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 *newversion, *pfile, *pext, *pvers; cchar *delim; int err, nvers, ii; STATB fstat; newversion = zstrdup(file,12); pfile = strrchr(newversion,'/'); // get filename if (! pfile) return 0; pext = strrchr(pfile,'.'); // get .ext if (! pext) return 0; if (strlen(pext) > 5) return 0; // .jpeg = 5 chars. pvers = pext - 4; // get curr. version: filename.vNN.ext if (! strmatchN(pvers,".v",2)) nvers = 0; else { err = convSI(pvers+2,nvers,1,98,&delim); // convert NN to number 1-98 if (err > 1) return 0; // conversion error if (delim != pext) nvers = 0; // check format is .vNN } if (nvers == 0) { // no version in file name pvers = pext; pext += 4; memmove(pext,pvers,6); // make space for .vNN before .ext strncpy(pvers,".vNN",4); } for (ii = 99; ii > nvers; ii--) // look for higher file versions { pvers[2] = ii/10 + '0'; // build filename.vNN.ext pvers[3] = ii - 10 * (ii/10) + '0'; err = stat(newversion,&fstat); if (! err) break; } if (ii == 99) return 0; // 99th version found ii++; // next version nvers = ii; pvers[2] = ii/10 + '0'; // build filename.vNN.ext pvers[3] = ii - 10 * (ii/10) + '0'; return newversion; } /**************************************************************************/ // 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[2] = { exif_orientation_key, exif_editlog_key }; cchar *exifdata[2]; char *ppv[1], *tempfile, *pext, *thumbfile; char edithist[exif_maxcc]; // edit history trail int nkeys, err, cc1, cc2, ii, ii0; int fmodified, fcopy; void (*menufunc)(GtkWidget *, cchar *); STATB fstat; cchar *warnalphamess = 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; } fmodified = 0; menufunc = 0; if (CEF) { // edit function active if (CEF->Fmods) fmodified = 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) fmodified = 1; // completed edits not saved outfile = zstrdup(outfile,6); // output file name pext = strrchr(outfile,'/'); // force compatible file extension if (pext) pext = strrchr(pext,'.'); // if not already if (! pext) pext = outfile + strlen(outfile); if (strmatch(outype,"jpg") && ! strcasestr(".jpg .jpeg",pext)) // standardize output file .ext strcpy(pext,".jpg"); // .jpg .tif .png if (strmatch(outype,"tif") && ! strcasestr(".tif .tiff",pext)) strcpy(pext,".tif"); if (strmatch(outype,"png") && ! strcasestr(".png",pext)) strcpy(pext,".png"); if (! fmodified) { // no unsaved edits fcopy = 1; // direct copy intput to output ? if (E0pxm) fcopy = 0; // non-edit change (upright) 15.03.1 if (! strmatch(curr_file_type,outype)) fcopy = 0; // no, file type change if (curr_file_bpc != outbpc) fcopy = 0; // no, BPC change if (jpeg_1x_quality < jpeg_def_quality) fcopy = 0; // no, higher compression wanted 15.03 if (fcopy) { if (strmatch(curr_file,outfile)) goto updateindex; // save to same file, skip copy err = shell_quiet("cp \"%s\" \"%s\"",curr_file,outfile); // copy unchanged file to output if (err) return 1; goto updateindex; } } Ffuncbusy = 1; // may be large file, slow CPU if (! E0pxm) { if (! resource_lock(Fpaintlock)) return 1; // 15.02 E0pxm = PXM_load(curr_file,1); // no prior edits, load image file resource_unlock(Fpaintlock); if (! E0pxm) { zfree(outfile); Ffuncbusy = 0; // PXM_load() diagnoses error return 1; } } tempfile = zstrdup(outfile,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 (no alpha channel) if (Fwarnalpha && E0pxm->nc > 3) { ii = zdialog_choose(Mwin,warnalphamess,Bsave,Bcancel,null); // 15.09 if (ii != 1) { remove(tempfile); // NO zfree(tempfile); zfree(outfile); Ffuncbusy = 0; return 1; } } err = PXM_ANY_save(E0pxm,tempfile); // (bpc = 8 always) outbpc = 8; } if (err) { if (*file_errmess) // pass error to user if (Fack) zmessageACK(Mwin,file_errmess); remove(tempfile); // failure, clean up zfree(tempfile); zfree(outfile); Ffuncbusy = 0; return 1; } exif_get(curr_file,&exifkey[1],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 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; } } exifdata[0] = ""; // remove EXIF orientation if present nkeys = 1; // (assume saved file is upright) if (cc1) { // prior and/or curr. edit hist 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,outfile); // rename temp file to output file if (err) { if (Fack) zmessageACK(Mwin,wstrerror(err)); remove(tempfile); // delete temp file zfree(tempfile); zfree(outfile); 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; thumbfile = image_thumbfile(outfile); // update thumbnail file if (thumbfile) zfree(thumbfile); updateindex: load_filemeta(outfile); // load output file metadata 15.02 update_image_index(outfile); // add output file to image index 15.08 if (navi::gallerytype == 1 && samedirk(outfile,curr_file)) { // output to curr. directory gallery(curr_file,"init"); // update curr. gallery list set_mwin_title(); // update posn, count in title } add_recent_file(outfile); // first in recent files list if (f_save_file) zfree(f_save_file); // actual file saved (.ext may change) f_save_file = outfile; strcpy(f_save_type,outype); // update f_save_xxx data f_save_bpc = outbpc; err = stat(outfile,&fstat); f_save_size = fstat.st_size; if (! strmatch(curr_file,outfile)) // restore metadata for curr. file 15.03 load_filemeta(curr_file); 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 *outfile = 0, *outfile2 = 0, *pp, *pext; int ii, zstat, err, yn; int bpc, mkcurr = 0; STATB fstat; zthreadcrash(); /*** _____________________________________________________________________________ | 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,"spin","jpgqual","hbft","10|100|1|90","space=3"); // 15.11 zdialog_add_widget(zd,"hbox","hbmc","dialog"); zdialog_add_widget(zd,"check","mkcurr","hbmc",0,"space=3"); zdialog_add_widget(zd,"label","labmc","hbmc",ZTX("make current"),"space=3"); zdialog_add_widget(zd,"label","labmc2","hbmc",ZTX("(new file becomes current file)"),"space=5"); zdialog_labelfont(zd,"labmc","sans bold",ZTX("make current")); // 15.11 if (! save_dirk) { // default from curr. file save_dirk = zstrdup(curr_file); pp = strrchr(save_dirk,'/'); *pp = 0; } gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(saveas_fchooser),save_dirk); char *fname = strrchr(curr_file,'/') + 1; gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(saveas_fchooser),fname); 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,500,500); zdialog_run(zd,f_save_as_dialog_event); 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 = '.'; // no replace .JPG with .jpg etc. strcpy(pext+1,type); } zdialog_fetch(zd,"mkcurr",mkcurr); // get make current option err = stat(outfile,&fstat); // 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"); // refresh gallery list curr_file_posn = gallery_position(curr_file,curr_file_posn); // update curr. file position set_mwin_title(); // update window title } 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 (strmatch(event,"enter")) zd->zstat = 1; if (strmatch(event,"escape")) zd->zstat = 2; // escape = cancel 15.07 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; } /**************************************************************************/ // open previous or next file in current gallery list // index is -1 or +1 void x_prev_next(int index) { char *pfile; int err; static int busy = 0; F1_help_topic = "prev_next"; if (busy) return; // avoid "function busy" 15.05 if (checkpend("busy block mods")) return; // 15.11 busy = 1; pfile = gallery_getnext(index,Flastversion); // get prev/next file (last version) if (! pfile) { Fpaint2(); zmessage_post(Mwin,2,ZTX("no more images")); // 15.08 goto returnx; } err = f_open(pfile,0,0,1,0); // open image or RAW file zfree(pfile); if (err) goto returnx; f_preload(index); // preload next image returnx: busy = 0; // 15.05 return; } void m_prev(GtkWidget *, cchar *menu) { if (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) { if (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; } /**************************************************************************/ // 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("Edit Functions Summary"))) showz_textfile("data","edit-menus"); 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"))) zmessageACK(Mwin,"%s\n %s\n %s\n %s\n %s\n %s\n", Frelease,Flicense,Fhomepage,Fsoftware,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. dirk directory path to search NF count of filespecs returned flist list of filespecs returned Fthumbs include thumbnail files if Fthumbs = 1 Finit initialize flag (omit, default = 1) (0 is used for internal recursive calls) Hidden directories and files (dotfiles) are suppressed. 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_count1; // filelist slots allocated int fif_count2; // filelist slots filled int find_imagefiles(cchar *dirk, char **&flist, int &NF, int Fthumbs, int Finit) { int flags = GLOB_NOSORT; int err, err2, cc, ftype; char *file, *mdirk, **templist; glob_t globdata; STATB statdat; if (Finit) fif_count1 = fif_count2 = 0; globdata.gl_pathc = 0; // glob() setup globdata.gl_offs = 0; globdata.gl_pathc = 0; NF = 0; flist = 0; mdirk = zstrdup(dirk,4); // append /* to input directory strcat(mdirk,"/*"); err = glob(mdirk,flags,null,&globdata); // find all files in directory if (err) { if (err == GLOB_NOMATCH) err = 0; else if (err == GLOB_ABORTED) err = 1; else if (err == GLOB_NOSPACE) err = 2; else err = 3; goto fif_return; } for (uint ii = 0; ii < globdata.gl_pathc; ii++) // loop found files { file = globdata.gl_pathv[ii]; err = stat(file,&statdat); if (err) continue; if (S_ISDIR(statdat.st_mode)) { // directory, call recursively err = find_imagefiles(file,flist,NF,Fthumbs,0); if (err) goto fif_return; continue; } ftype = image_file_type(file); // check file type is image, if (ftype < 2 || ftype > 4) continue; // RAW, or thumbnail file if (! Fthumbs && ftype == 4) continue; // exclude thumbnail files if (fif_count2 == fif_count1) { // output list is full if (fif_count1 == 0) { fif_count1 = 1000; // initial space, 1000 files cc = fif_count1 * sizeof(char *); fif_filelist = (char **) zmalloc(cc); } else { templist = fif_filelist; // expand by 2x each time needed cc = fif_count1 * sizeof(char *); fif_filelist = (char **) zmalloc(cc+cc); memcpy(fif_filelist,templist,cc); memset(fif_filelist+fif_count1,0,cc); zfree(templist); fif_count1 *= 2; } } fif_filelist[fif_count2] = zstrdup(file); // add file to output list fif_count2 += 1; } err = 0; fif_return: err2 = errno; // preserve Linux errno globfree(&globdata); // free memory zfree(mdirk); if (Finit) { NF = fif_count2; if (NF) flist = fif_filelist; } errno = err2; return err; } /************************************************************************** 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, ftyp; cchar *pext; PXB *pxb; STATB fstat; err = stat(file,&fstat); if (err) { if (Fack) zmessageACK(Mwin,ZTX("file not found: %s"),file); goto f_load_fail; } ftyp = image_file_type(file); if (ftyp != 2 && ftyp != 3) { if (Fack) zmessageACK(Mwin,ZTX("file type not supported: %s"),file); goto f_load_fail; } f_load_size = fstat.st_size; // set f_load_size pext = strrchr(file,'/'); if (! pext) pext = file; pext = strrchr(pext,'.'); if (! pext) pext = ""; if (ftyp == 2) { 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 (ftyp == 3) strcpy(f_load_type,"RAW"); // f_load_type = RAW 15.08 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, ftyp; cchar *pext; PXM *pxm; STATB fstat; err = stat(file,&fstat); if (err) { if (Fack) zmessageACK(Mwin,ZTX("file not found: %s"),file); goto f_load_fail; } ftyp = image_file_type(file); if (ftyp != 2 && ftyp != 3) { if (Fack) zmessageACK(Mwin,ZTX("file type not supported: %s"),file); goto f_load_fail; } f_load_size = fstat.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 (ftyp == 3) // 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_limit || hh > wwhh_limit) { snprintf(file_errmess,999,"image too big, %dx%d: %s",ww,hh,file); printz("%s\n",file_errmess); 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; // 15.08 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; // bugfix 15.09.2 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 (ww > wwhh_limit || hh > wwhh_limit) { snprintf(file_errmess,999,"image too big, %dx%d: %s",ww,hh,file); printz("%s\n",file_errmess); 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_PXM_load(file); // fallback to pixbuf loader } if (nc == 2 || nc == 4) ac = 1; // alpha channel 15.08 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); // 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; } pxm = PXM_make(ww,hh,3+ac); // create PXM 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]; pix += 3+ac; tiff8 += 4; } } zfree(tiffbuff); return pxm; } pxm = PXM_make(ww,hh,3+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); 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; int tiffstat = 0; int ww, hh, row, col, rowcc; // int not uint int nc, ac, pm = 2, pc = 1, comp = 5; char *tiffbuff; 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; // 15.08 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); // LZW 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_limit || hh > wwhh_limit) { png_destroy_read_struct(&pngstruct,&pnginfo,0); snprintf(file_errmess,999,"image too big, %dx%d: %s",ww,hh,file); printz("%s\n",file_errmess); 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 15.08 } 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 15.08 } 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 15.08 } 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 (ww > wwhh_limit || hh > wwhh_limit) { png_destroy_read_struct(&pngstruct,&pnginfo,0); snprintf(file_errmess,999,"image too big, %dx%d: %s",ww,hh,file); printz("%s\n",file_errmess); return 0; } 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 (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) { // 15.09 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; // 15.08 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); // 15.09 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; zthreadcrash(); *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); // 15.08 ac = gdk_pixbuf_get_has_alpha(pixbuf); rs = gdk_pixbuf_get_rowstride(pixbuf); pixels1 = gdk_pixbuf_get_pixels(pixbuf); if (ww > wwhh_limit || hh > wwhh_limit) { snprintf(file_errmess,999,"image too big, %dx%d: %s",ww,hh,file); printz("%s\n",file_errmess); 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]; zthreadcrash(); 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); // 15.08 ac = gdk_pixbuf_get_has_alpha(pixbuf); rowst = gdk_pixbuf_get_rowstride(pixbuf); pixels1 = gdk_pixbuf_get_pixels(pixbuf); if (ww > wwhh_limit || hh > wwhh_limit) { snprintf(file_errmess,999,"image too big, %dx%d: %s",ww,hh,file); printz("%s\n",file_errmess); g_object_unref(pixbuf); return 0; } pxm = PXM_make(ww,hh,3+ac); 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; zthreadcrash(); *file_errmess = 0; ww = pxm->ww; hh = pxm->hh; nc = pxm->nc; // 15.08 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 15.09 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) ***************************************************************************/ // Load PXB (pixbuf, 8 bit color) from RAW file using DCraw program // f_load_bpc is set to 16 PXB * RAW_PXB_load(cchar *rawfile) { PXB *pxb; int handle, err; char *pp, rawcommand[200]; strcpy(rawcommand,DCrawcommand); // change default DCraw command to pp = strstr(rawcommand,"-6 "); // output tif-8 instead of tif-16 if (pp) strcpy(pp, DCrawcommand + (pp+3-rawcommand)); handle = shell_asynch("%s -c \"%s\" >temp.tif",rawcommand,rawfile); // convert RAW to tif-8 file while (true) { err = shell_asynch_status(handle); if (err != -1) break; zsleep(0.01); zmainloop(); } pxb = TIFF_PXB_load("temp.tif"); // load the tiff file f_load_bpc = 16; // RAW file bpc remove("temp.tif"); // delete the tiff file return pxb; } // Load PXM (float color) from RAW file using DCraw program // f_load_bpc is set to 16 PXM * RAW_PXM_load(cchar *rawfile) { PXM *pxm; int handle, err; handle = shell_asynch("%s -c \"%s\" >temp.tif",DCrawcommand,rawfile); // convert RAW to tiff-16 file while (true) { err = shell_asynch_status(handle); if (err != -1) break; zsleep(0.1); zmainloop(); } pxm = TIFF_PXM_load("temp.tif"); // load the tiff file f_load_bpc = 16; remove("temp.tif"); // delete the tiff file return pxm; }